Tag Archives: Serialisation

My object won’t deserialise using System.Text.Json

I imagine this is a very common scenario, so I thought it worth documenting.

Let’s take the following controller method in a bog standard Asp.Net Core API:

        public async Task<DataContainer> Get()
        {
            var data = Enumerable.Range(1, 5).Select(index => new MyData
            {
                Value1 = index,
                Value2 = "some other value"
            })
            .ToList();

            return new DataContainer()
            {
                Data = data
            };
        }

Here’s a method that consumes it:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream);

So, you’ve expect this to work, right? It makes no difference what DataContainer looks like, because you’re serialising and deserialising using the same technology. In fact, it just returns a correctly structured object, but every value will be null.

I’ll tell you what does work, is this:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            var responseString = await result.Content.ReadAsStringAsync();
            var data = Newtonsoft.Json.JsonConvert.DeserializeObject<DataContainer>(responseString);             

If you look at the JSON, it looks kosher enough (again, the exact structure is irrelevant). So, what’s the issue?

Interestingly, when the JSON is serialised, it is serialised into camelCase; so if you have a variable such as MyData, it will get serialised as myData. Newtonsoft deals with this, because it’s a common use case in .Net; however, System.Text.Json.Deserialize* assumes that the casing will match the object!

This is odd considering it changes it on the way out!!

Okay, so what’s the fix?

You just tell Deserialize* to use camelCase:

            var client = _clientFactory.CreateClient();
            var result = await client.GetAsync($"https://localhost:22222/endpoint");

            using var responseStream = await result.Content.ReadAsStreamAsync();

            var options = new JsonSerializerOptions()
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };
            var data = await JsonSerializer.DeserializeAsync<DataContainer>(responseStream, options);

Annoying, eh?

References

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson

https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to#use-camel-case-for-all-json-property-names

MVVM Cross Basics – Passing Complex Parameters During Navigation

Having covered basic navigation, I found that I needed to pass my game state from view-model to view-model. I initially thought that I could simply do this as follows:

    private void CallVM2()
    {
        MyObj newObj = new MyObj();
        IMyService myService = new MyService();

		Dictionary<string, object> p = new Dictionary<string, object>()
        {
            {"MyObj", newObj},
            {"MyService", myService}
        };
		ShowViewModel<ViewModel2>(p);
    }

The code for ViewModel2:

    public void Init(Dictionary<string, object> p)
    {
    }

Anyway, it turns out you can’t. Init does get fired, but if you pass anything more complex than a string, it just bins the parameter.

The answer appears to be to create a class and to serialise it using JSON.NET:

http://stackoverflow.com/questions/19058173/passing-complex-navigation-parameters-with-mvvmcross-showviewmodel

After implementing this suggestion, I ended up with something like this:

    public abstract class BaseViewModel : MvxViewModel
    {
        private const string ParameterName = "parameter";

        public BaseViewModel()
        {
        }

        protected void ShowViewModel<TViewModel>(object parameter) where TViewModel : IMvxViewModel
        {
            var text = Mvx.Resolve<IMvxJsonConverter>().SerializeObject(parameter);
            base.ShowViewModel<TViewModel>(text);
        }

        public void Init(string parameter)
        {
            if (parameter == null || parameter.Length == 0) return;

            IMvxJsonConverter converter = Mvx.Resolve<IMvxJsonConverter>();
            NavigationParameter deserialized = converter.DeserializeObject<NavigationParameter>(parameter);
            RealInit(deserialized);
            
        }
    }

If you don’t register the converter in the IoC container (as I initially didn’t), then you’ll get this error:

A first chance exception of type ‘Cirrious.CrossCore.Exceptions.MvxIoCResolveException’ occurred in Cirrious.CrossCore.DLL

Additional information: Failed to resolve type Cirrious.CrossCore.Platform.IMvxJsonConverter

The fix:

    public abstract class BaseViewModel : MvxViewModel
    {
        private const string ParameterName = "parameter";

        public BaseViewModel()
        {
            Mvx.RegisterType<IMvxJsonConverter, MvxJsonConverter>();
        }

Now, if you override the `RealInit` method, you’ll see that the parameters are, indeed, available. However, I had a further problem; this time being with JSON.NET. As you can see from the above structure, it’s a dictionary of objects. So, when running this, it will certainly pass through the object, but it’ll just be the serialised string.

And…?

I’m pretty sure that I could get around this using a combination of generics and a custom Javascript serialisation library, but I’ve got a game to write, so I’m going to stick with declaring the Navigation parameter as follows:

   public class NavigationParameter
    {
        public MyObject ObjectParameter { get; set; }
        public MyService ServiceParameter { get; set; } 
    }

It’s worth bearing in mind that you can’t use interfaces either in the parameters; JSON.NET needs a concrete class.