Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Property-based type resolution in JSON.NET

Tags:

c#

.net

json.net

Is it possible to override the type resolution using JSON.NET based on a property of the JSON object? Based on existing APIs, it looks like I need a way of accepting a JsonPropertyCollection and returning the Type to be created.

NOTE: I'm aware of the TypeNameHandling attribute, but that adds a $type property. I do not have control over the source JSON.

like image 547
Richard Szalay Avatar asked Aug 09 '11 14:08

Richard Szalay


1 Answers

It would appear that this is handled by creating a custom JsonConverter and adding it to JsonSerializerSettings.Converters before deserialisation.

nonplus has left a handy sample on the JSON.NET discussions board on Codeplex. I've modified the sample to return custom Type and deferring to the default creation mechanism, rather than creating the object instance on the spot.

abstract class JsonCreationConverter<T> : JsonConverter
{
    /// <summary>
    /// Create an instance of objectType, based properties in the JSON object
    /// </summary>
    protected abstract Type GetType(Type objectType, JObject jObject);

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, 
        object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);

        Type targetType = GetType(objectType, jObject);

        // TODO: Change this to the Json.Net-built-in way of creating instances
        object target = Activator.CreateInstance(targetType);

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

        return target;
    }
}

And here is the example usage (also updated as mentioned above):

class VehicleConverter : JsonCreationConverter<Vehicle>
{
    protected override Type GetType(Type objectType, JObject jObject)
    {
        var type = (string)jObject.Property("Type");
        switch (type)
        {
            case "Car":
                return typeof(Car);
            case "Bike":
                return typeof(Bike);
        }

        throw new ApplicationException(String.Format(
            "The given vehicle type {0} is not supported!", type));
    }
}
like image 105
Richard Szalay Avatar answered Sep 23 '22 17:09

Richard Szalay