I solved a similar problem and I found a solution. This is not very elegant, and I think there should be a better way, but at least it works. So my idea was to have a JsonConverter for each type that implements IBar and one converter for IBar .
So, let's start with the models:
public interface IBar { } public class BarA : IBar { } public class Foo { public IBar Bar { get; set; } }
Now create a converter for IBar . It will only be used when deserializing JSON. It will try to read the $type variable and the call converter to implement the type:
public class BarConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObj = JObject.Load(reader); var type = jObj.Value<string>("$type"); if (type == GetTypeString<BarA>()) { return new BarAJsonConverter().ReadJson(reader, objectType, jObj, serializer); }
And this is the converter for the BarA class:
public class BarAJsonConverter : BarBaseJsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
You may notice that it is inherited from the BarBaseJsonConverter class, not JsonConverter . Also, we do not use the serializer parameter in the WriteJson and ReadJson . There is a problem using the serializer parameter inside custom converters. You can read it here. We need to create a new instance of JsonSerializer , and the base class is a good candidate for this:
public abstract class BarBaseJsonConverter : JsonConverter { public JsonSerializer GetSerializer() { var serializerSettings = JsonHelper.DefaultSerializerSettings; serializerSettings.TypeNameHandling = TypeNameHandling.Objects; var converters = serializerSettings.Converters != null ? serializerSettings.Converters.ToList() : new List<JsonConverter>(); var thisConverter = converters.FirstOrDefault(x => x.GetType() == GetType()); if (thisConverter != null) { converters.Remove(thisConverter); } serializerSettings.Converters = converters; return JsonSerializer.Create(serializerSettings); } }
JsonHelper is just a class for creating JsonSerializerSettings :
public static class JsonHelper { public static JsonSerializerSettings DefaultSerializerSettings { get { return new JsonSerializerSettings { Converters = new JsonConverter[] { new BarConverter(), new BarAJsonConverter() } }; } } }
Now it will work, and you can still use your custom converters for serialization and deserialization:
var obj = new Foo { Bar = new BarA() }; var json = JsonConvert.SerializeObject(obj, JsonHelper.DefaultSerializerSettings); var dObj = JsonConvert.DeserializeObject<Foo>(json, JsonHelper.DefaultSerializerSettings);