JsonConverter custom inheritance not working when using JsonConverterAttribute

I am trying to deserialize a derived type, and I want to use a custom property of 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 . In ReadJson I read the Type property and instantiate this type using the ToObject<T> function. Everything is working fine until I use JsonConverterAttribute . The ReadJson method runs endlessly because the attribute applies to subtypes as well.

How to prevent the use of this attribute for 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 } } 
+3
Aug 20 '14 at 12:02
source share
2 answers

Remove the [JsonConverter(typeof(TypeSerializer))] attribute from the Base class and replace the following line in the Deserialize test:

 JObject.Parse(json).ToObject<Base>(); // Crash 

with this:

 var obj = JsonConvert.DeserializeObject<Base>(json, new TypeSerializer()); 

UPDATE 1 This update matches the comment from the questionnaire:

Leave the [JsonConverter(typeof(TypeSerializer))] attribute on the Base class. Use the following line to deserialize:

 var obj = JsonConvert.DeserializeObject<Base>(json); 

and change the ReadJson method as follows:

 public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var j = JObject.Load(reader); if (j["Type"].ToString() == "a") return new AType(int.Parse(j["Height"].ToString())); return new BType(j["Name"].ToString()); } 
+3
Aug 20 '14 at 12:55
source share

I had a very similar problem with the project I'm currently working on: I wanted to create a custom JsonConverter and map it to my objects via attributes, but then the code got into an endless loop.

That trick in my case used serializer.Populate instead of JObject.ToObject (I could not use .ToObject , even if I wanted to, I use 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; } 
+3
Mar 30 '16 at 0:28
source share



All Articles