Last active
February 17, 2023 23:16
-
-
Save chrisoldwood/b604d69543a5fe5896a94409058c7a95 to your computer and use it in GitHub Desktop.
Example showing infinite loop deserialising polymorphic types with a custom JsonConverter.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace JsonTests | |
{ | |
public class CustomJsonConverter : JsonConverter | |
{ | |
public override bool CanConvert(Type objectType) | |
{ | |
return typeof(BaseType).IsAssignableFrom(objectType); | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |
{ | |
JObject item = JObject.Load(reader); | |
#if USE_TO_OBJECT | |
var @object = item.ToObject<DerivedType>(); | |
return @object; | |
#else // USE_POPULATE | |
var @object = new DerivedType(); | |
serializer.Populate(item.CreateReader(), @object); | |
return @object; | |
#endif | |
} | |
public override bool CanWrite => false; | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace JsonTests | |
{ | |
[JsonConverter(typeof(CustomJsonConverter))] | |
public abstract class BaseType | |
{ | |
public string Value { get; set; } | |
} | |
public class DerivedType : BaseType | |
{ | |
public string DerivedValue { get; set; } | |
} | |
public class Container | |
{ | |
public List<BaseType> Objects { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace JsonTests | |
{ | |
[TestFixture] | |
public class Tests | |
{ | |
[Test] | |
public void Test() | |
{ | |
const string json = | |
"{ 'Objects': [" + | |
" { 'Value': 'test value' } " + | |
"] }"; | |
#if USE_TO_OBJECT | |
Assert.That(() => JsonConvert.DeserializeObject<Container>(json), | |
Throws.InstanceOf<StackOverflowException>()); | |
#else // USE_POPULATE | |
Assert.That(() => JsonConvert.DeserializeObject<Container>(json), | |
Throws.Nothing); | |
#endif | |
} | |
} | |
} |
Thanks for the info. However, the stack overflow answers you cite did not use the attribute on the base type. They inject the converters upon serializer creation so that they do not affect the subtypes. And maybe it's important to only check the base type in CanConvert, too, depending on the use case.
Thanks Chris, this was very helpful. I had an example where I was using the toObject method in JsonSerializerSettings and it worked so I was surprised when my new code used that method and was causing the stack overflow exception.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This Gist shows two techniques listed on Stack Overflow on how to handle the deserialisation of polymorphic types with Newtonsoft's JSON.Net. The use of
ToObject
above inReadJson
results in an infinite loop and subsequently a stack overflow, whereas the use ofPopulate
works as desired. TheToObject
approach is the one described in Deserializing polymorphic json classes without type information using json.net whereas the other one comes from Custom inheritance JsonConverter fails when JsonConverterAttribute is used [*]. A third example from Stack Overflow that suggests the latter approach is the preferred one is Polymorphic JSON Deserialization failing using Json.Net.To invoke the stack overflow behaviour in the example above use
#define USE_TO_OBJECT
, whereas not defining it will use the working approach. I don't understand why the technique suggested in Deserializing polymorphic json classes without type information using json.net doesn't work for me here. I guess I'm doing something wrong, or subtly different, as otherwise the answer would surely be down-voted? Or perhaps JSON.Net has acquired a bug (or had one fixed) since the original answer was given?Either way if you know what's going on I'd sure appreciate a comment explaining what I'm missing here :o).
[*] In another example (at work) I managed to get it working when manually adding the custom converter to the
JsonSerializerSettings
object but also got a failure when using the attribute based approach. In the above example it fails in both scenarios.