Custom inheritance JsonConverter fails when JsonConverterAttribute is used

c#, json.net

Solution

I had a very similar problem with a project that I am currently working on: I wanted to make a custom `JsonConverter` and map it to my entities via attributes, but then the code got trapped in an infinite loop.

What did the trick in my case was using `serializer.Populate` instead of `JObject.ToObject` (I couldn't use `.ToObject` even if I wanted to; I am using version 3.5.8, in which this function does not exist). Below is my `ReadJson` method as an example:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    JContainer lJContainer = default(JContainer);

    if (reader.TokenType == JsonToken.StartObject)
    {
        lJContainer = JObject.Load(reader);
        existingValue = Convert.ChangeType(existingValue, objectType);
        existingValue = Activator.CreateInstance(objectType);

        serializer.Populate(lJContainer.CreateReader(), existingValue);
    }

    return existingValue;
}

Problem

I am trying to deserialize derived type, and I want to use a custom property `Type` to distinguish between derived types. ``` [ { "Type": "a", "Height": 100 }, { "Type": "b", "Name": "Joe" } ] ``` The solution I came to was to create a custom `JsonConverter`. On `ReadJson` I read the `Type` property and instantiate that type through the `ToObject<T>` function. Everything works fine until I use a `JsonConverterAttribute`. The `ReadJson` method loops infinitely because the attribute is applied on subtypes too. How do I prevent this attribute from being applied to the subtypes? ``` [JsonConverter(typeof(TypeSerializer))] public abstract class Base { private readonly string type; public Base(string type) { this.type = type; } public string Type { get { return type; } } } public class AType : Base { private readonly int height; public AType(int height) : base("a") { this.height = height; } public int Height { get { return height; } } } public class BType : Base { private readonly string name; public BType(string name) : base("b") { this.name = name; } public string Name { get { return name; } } } public class TypeSerializer : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(Base); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var j = JObject.Load(reader); var type = j["Type"].ToObject<string>(); if (type == "a") // Infinite Loop! StackOverflowException return j.ToObject<AType>(); if (type == "b") return j.ToObject<BType>(); throw new NotImplementedException(type); } } [TestFixture] public class InheritanceSerializeTests { [Test] public void Deserialize() { var json = @"{""Type"":""a"", ""Height"":100}"; JObject.Parse(json).ToObject<Base>(); // Crash } } ```

Original source