Deserializing different types based on properties, with Newtonsoft.Json

Sometimes we're presented objects in JSON that do not directly map to a strongly typed object. An example of that is given here:

[{
        "type" : "Car",
        "wheels" : 4,
        "trunk" : true
    }, {
        "type" : "Bicycle",
        "wheels" : 2,
        "persons" : 1
    }
]

This example is very simple, but it presents a fun little problem. How can we map the above into two types, Car and Bicycle, each deriving from a base class Vehicle?

JsonConverter

The JsonConverter is a helper class which can assist in converting other types than what Newtonsoft.Json comes with out of the box. They basically encompass the process of getting "json object -> .NET object" and back again. We'll be using one of these to map our types back and forth.

public class VehicleConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken jObject = JToken.ReadFrom(reader);
        VehicleType type = jObject["type"].ToObject<VehicleType>();

        Vehicle result;
        switch (type)
        {
            case VehicleType.Car:
                result = new Car();
                break;
            case VehicleType.Bicycle:
                result = new Bicycle();
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }

        serializer.Populate(jObject.CreateReader(), result);

        return result;
    }

    // Skipped WriteJson and CanConvert
}

In the implementation above, we switch the target type based on our reading of the VehiceType enum value. We then use the Populate method from the JsonSerializer to populate our object, as calling Deserialize would once more call our own Converter which eventually leads to a stack overflow [1].

We can adjust this approach any way we'd like. For example we could: Automatically find applicable types by searching the assembly, register our types dynamically through some management system etc.

Caveats

Currently, we can't serialize in a simple way. As with before, if we were to call Serialize directly, we'd end up calling ourselves. There is a workaround though - in my example I've registered the JsonConverter directly on the Vehicle type. If we were instead to implement CanConvert and register our converter with a JsonSerializerSettings object, we'd be able to not use the converter when writing.

Example code

I've put the source code for the examples above online here: github.com/LordMike/blog-examples/deserialize-different-types.

Example

[1] Actually, Newtonsoft detects this and throws a JsonSerializationException, stating that a Self referencing loop detected