using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
public static void Main()
JsonTypeConverterProblem problem = new JsonTypeConverterProblem();
problem.ShowSerializationBug();
problem.DeserializationWorks();
public string Id { get; set; }
public A Child { get; set; }
public sealed class JsonTypeConverterProblem
public void ShowSerializationBug()
Child = new C() { Id = "bar" }
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.ContractResolver = new TypeHintContractResolver();
string json = JsonConvert.SerializeObject(a, Formatting.Indented, jsonSettings);
Assert.Contains(@"""Target"": ""B""", json);
Assert.Contains(@"""Is"": ""C""", json);
public void DeserializationWorks()
JsonSerializerSettings jsonSettings = new JsonSerializerSettings();
jsonSettings.ContractResolver = new TypeHintContractResolver();
A a = JsonConvert.DeserializeObject<A>(json, jsonSettings);
Assert.IsType<C>(a.Child);
public class TypeHintContractResolver : DefaultContractResolver
public override JsonContract ResolveContract(Type type)
JsonContract contract = base.ResolveContract(type);
if ((contract is JsonObjectContract)
&& ((type == typeof(A)) || (type == typeof(B))) || (type == typeof(C)))
contract.Converter = new TypeHintJsonConverter(type);
public class TypeHintJsonConverter : JsonConverter
private readonly Type _declaredType;
public TypeHintJsonConverter(Type declaredType)
_declaredType = declaredType;
public override bool CanConvert(Type objectType)
return objectType == _declaredType;
private Type TypeFromTypeHint(JObject jo)
if (new JValue("B").Equals(jo["Target"]))
else if (new JValue("A").Equals(jo["Hint"]))
else if (new JValue("C").Equals(jo["Is"]))
throw new ArgumentException("Type not recognized from JSON");
private JProperty TypeHintPropertyForType(Type type)
return new JProperty("Hint", "A");
else if (type == typeof(B))
return new JProperty("Target", "B");
else if (type == typeof(C))
return new JProperty("Is", "C");
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (! CanConvert(objectType))
throw new InvalidOperationException("Can't convert declaredType " + objectType + "; expected " + _declaredType);
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.Null)
if (jToken.Type != JTokenType.Object)
throw new InvalidOperationException("Json: expected " + _declaredType + "; got " + jToken.Type);
JObject jObject = (JObject) jToken;
Type deserializingType = TypeFromTypeHint(jObject);
var target = Activator.CreateInstance(deserializingType);
serializer.Populate(jObject.CreateReader(), target);
public override bool CanWrite
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
JProperty typeHintProperty = TypeHintPropertyForType(value.GetType());
JObject jo = new JObject();
if (typeHintProperty != null)
jo.Add(typeHintProperty);
foreach (PropertyInfo prop in value.GetType().GetProperties())
object propValue = prop.GetValue(value);
jo.Add(prop.Name, JToken.FromObject(propValue, serializer));