markneustadt.com

Inherited Object Serialization with Newtonsoft.JSON

Wow… I’m coming up with some sexy titles for posts lately.

This is going to be how to deserialize JSON objects into native C# objects using Newtonsoft.JSON.  Serialization of objects to JSON is very straightforward.  Serializing objects inherited from a base class is pretty simple, but there’s a little bit of doing to do first. Converting those items back though… that’s a trick.

Let’s start off by considering a simple class diagram.  We’ll start with an animal class.  From there, we’ll derive mammals and birds.  Then off into dogs, cats, robins and eagles.

ClassDiagram

Pretty easy, right?  In order to create a few objects, we might use some code like this:


Dog dog = new Dog() { breed="Beagle", furLength=1.5, landBased=true, numberOfLimbs=4, weight=18.0 };

Then we can serialize that into a JSON string very easily.


string dogString = JsonConvert.SerializeObject(dog);

This results in a JSON string object like this:


{
 "breed": "Beagle",
 "furLength": 1.5,
 "landBased": true,
 "numberOfLimbs": 4,
 "weight": 18.0
}

If we try to deserialize that back to a Dog object it works just fine.

Dog newDog = JsonConvert.DeserializeObject<Dog>(dogString);

The problem arises when we don’t know what kind of Animal we’re deserializing.  If we try this operation:

Animal newAnimal = JsonConvert.DeserializeObject<Animal>(dogString);

we get the following exception:

Could not create an instance of type PolymorphicJson.Classes.Animal. Type is an interface or abstract class and cannot be instantiated

And it’s true… an examination of the code reveals that Animal has indeed been declared as Abstract and thus can’t be instantiated.  But the string is a valid animal.

How do we get the program to deserialize the string back into the animal we created to begin with?  There are a few things we need to do.

Serialize the object correctly

We need to serialize the object correctly.  The SerializeObject method of the JsonConverter class can take some additional parameters which will control the formatting and object type handling.  Instead of simply passing the Dog object as we did earlier, if we pass these extra parameters, we’ll get the necessary additional information in the output string.


string dogString = JsonConvert.SerializeObject(dog, Formatting.Indented, new JsonSerializerSettings
{
 TypeNameHandling = TypeNameHandling.Objects,
 TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
});

 

{
 "$type": "PolymorphicJson.Classes.Dog, PolymorphicJson",
 "breed": "Beagle",
 "furLength": 1.5,
 "landBased": true,
 "numberOfLimbs": 4,
 "weight": 18.0
}

Notice the string now has an extra “$type” property.  This is going to be used to determine what kind of thing we’re deserializing.  Newtonsoft.Json doesn’t know what our classes are though.  We need to create a custom converter.

Create a custom JsonConverter

The stock JsonConverter class is great at handling the standard types found in the .Net framework.  It doesn’t know how to deserialize our custom classes though.  Fortunately, we can create custom converters that will do the job for us.


/// <summary>
/// Custom converter to convert objects to and from JSON
/// </summary>
/// <typeparam name="T">The type of object being passed in</typeparam>
public abstract class CustomJsonConverter<T> : JsonConverter
{
 /// <summary>
 /// Abstract method which implements the appropriate create method
 /// </summary>
 /// <param name="objectType"></param>
 /// <param name="jsonObject"></param>
 /// <returns></returns>
 protected abstract T Create(Type objectType, JObject jsonObject);

 /// <summary>
 /// Determines whether an instance of the current System.Type can be assigned from an instance of the specified Type.
 /// </summary>
 /// <param name="objectType"></param>
 /// <returns></returns>
 public override bool CanConvert(Type objectType)
 {
 return typeof(T).IsAssignableFrom(objectType);
 }

 /// <summary>
 /// Reads JSON and returns the appropriate object
 /// </summary>
 /// <param name="reader"></param>
 /// <param name="objectType"></param>
 /// <param name="existingValue"></param>
 /// <param name="serializer"></param>
 /// <returns></returns>
 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
 {
 // load the json string
 var jsonObject = JObject.Load(reader);

 // instantiate the appropriate object based on the json string
 var target = Create(objectType, jsonObject);

 // populate the properties of the object
 serializer.Populate(jsonObject.CreateReader(), target);

 // return the object
 return target;
 }

 /// <summary>
 /// Creates the JSON based on the object passed in
 /// </summary>
 /// <param name="writer"></param>
 /// <param name="value"></param>
 /// <param name="serializer"></param>
 public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
 {
 throw new NotImplementedException();
 }
}

/// <summary>
/// The converter to use when deserializing animal objects
/// </summary>
public class AnimalConverter : CustomJsonConverter<Animal>
{
 /// <summary>
 /// The class that will create Animals when proper json objects are passed in
 /// </summary>
 /// <param name="objectType"></param>
 /// <param name="jsonObject"></param>
 /// <returns></returns>
 protected override Animal Create(Type objectType, JObject jsonObject)
 {
 // examine the $type value
 string typeName = (jsonObject["$type"]).ToString();

 // based on the $type, instantiate and return a new object
 switch (typeName)
 {
 case "PolymorphicJson.Classes.Dog, PolymorphicJson":
 return new Dog();
 case "PolymorphicJson.Classes.Cat, PolymorphicJson":
 return new Cat();
 case "PolymorphicJson.Classes.Robin, PolymorphicJson":
 return new Robin();
 case "PolymorphicJson.Classes.Eagle, PolymorphicJson":
 return new Eagle();
 default:
 return null;
 }
 }
}

There are two classes here.  First, we create a class called the CustomJsonConverter.  This is an abstract class that requires the type be part of the construction call.  It implements some base functionality that will do the work of reading the JSON passed in and returning the deserialized object.

The second class is what actually examines the JSON to determine what type of object is being passed in.  Specifically, it looks for that new $type property we got when we serialized the object with our JsonSerializerSettings.  Based on the type found there, it instantiates the appropriate object.  That object is then populated in the CustomJsonConverter.

All of this is used when we deserialize the string.  When we serialized the object, we had to do a little extra.  The same way, when we deserialize the string to an object, we have to do a little extra.

Deserialize the string

Rather than just deserialize the string into an “Animal”, we have to pass the proper converter to the settings and then pass the settings to the DeserializeObject method.

var eventConverter = new AnimalConverter();
var deseralizeSettings = new JsonSerializerSettings();
deseralizeSettings.Converters.Add(eventConverter);
deseralizeSettings.TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
deseralizeSettings.TypeNameHandling = TypeNameHandling.Auto;

Animal someAnimal = JsonConvert.DeserializeObject<Animal>(jsonAnimalString, deseralizeSettings);

When we’re done with that operation, we’ll have an Animal which actually is a “Dog”, “Cat”, “Robin” or “Eagle”.

From there we can call some operation on the “someAnimal” object or we can cast the object to the appropriate type and continue with our operations.

Download the project

 

 

 

 

 

 

 

 

Scroll To Top