Using Json.NET converters to deserialize properties

One of the things you can do with Json.NET is:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;

JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

The TypeNameHandling flag will add a $type property to the JSON, which allows Json.NET to know which concrete type it needs to deserialize the object into. This allows you to deserialize an object while still fulfilling an interface or abstract base class.

The downside, however, is that this is very Json.NET-specific. The $type will be a fully-qualified type, so if you're serializing it with type info,, the deserializer needs to be able to understand it as well.

Documentation: Serialization Settings with Json.NET


You can achieve this through the use of the JsonConverter class. Suppose you have a class with an interface property;

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(TycoonConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

Your JsonConverter is responsible for serializing and de-serializing the underlying property;

public class TycoonConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return (objectType == typeof(IPerson));
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    return serializer.Deserialize<Tycoon>(reader);
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

When you work with an Organisation deserialized via Json.Net the underlying IPerson for the Owner property will be of type Tycoon.


Instead of passing a customized JsonSerializerSettings object to JsonConvert.SerializeObject() with the TypeNameHandling.Objects option, as previously mentioned, you can just mark that specific interface property with an attribute so the generated JSON wouldn't be bloated with "$type" properties on EVERY object:

public class Foo
{
    public int Number { get; set; }

    // Add "$type" property containing type info of concrete class.
    [JsonProperty( TypeNameHandling = TypeNameHandling.Objects )]
    public ISomething { get; set; }
}

In the most recent version of the third party Newtonsoft Json converter you can set a constructor with a concrete type relating to the interfaced property.

public class Foo
{ 
    public int Number { get; private set; }

    public ISomething IsSomething { get; private set; }

    public Foo(int number, Something concreteType)
    {
        Number = number;
        IsSomething = concreteType;
    }
}

As long as Something implements ISomething this should work. Also do not put a default empty constructor in case the JSon converter attempts to use that, you must force it to use the constructor containing the concrete type.

PS. this also allows you to make your setters private.