JsonKnownTypes
JsonKnownTypes copied to clipboard
Deserializing Existing Json with Basic Example Results in Exception
Describe the bug When using JsonKnownTypes on existing json content, adding a new derived type and then decorating the base class with the basic [JsonConverter...] attribute results in an exception:
To Reproduce Steps to reproduce the behavior:
- Create a base class
- Serialize this class using standard Newtonsoft behaviors, no type handling
- Implement a new derived class
- Decorate the base class with the JsonConverter for JsonKnownTypes<Base>
- Deserialize
Expected behavior I would expect an instance without a discriminator to automatically deserialize to the base class
Screenshots If applicable, add screenshots to help explain your problem.
Additional context
Exception content:
JsonKnownTypes.Exceptions.JsonKnownTypesException:
I then tried adding [JsonKnownTypeFallback(typeof(Base))] This results in another exception: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidCastException: Unable to cast object of type 'Base' to type 'Derived'.
Two unexpected behaviors here!
I would be very happy to help implement a solution here!
What about that exception
Unable to cast object of type 'Base' to type 'Derived'.
can you pls provide exact code(just past all code here), cause its a bit weird why it cast Base to Derived.
Some more context: I have old data that was deserialized as one type. A derived type was later added. After setting up testing, I'm not entirely sure I agree with myself about scenario 2. Perhaps the shown behavior is OK. Deserializing as the base type works fine. I do think scenario 1 is valid, though!
using JsonKnownTypes;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonKnownTypesRepro
{
[TestClass]
public class KnownTypesDerivedExceptionRepro
{
[JsonConverter(typeof(JsonKnownTypesConverter<Base>))]
public class Base
{
public string BaseProp { get; set; }
}
public class Derived : Base
{
public string DerivedProp { get; set; }
}
[TestMethod]
public void TestLegacyBaseClassDeserialization()
{
//in the scenario where a base type was serialized in old code, before the jsonknowntype was associated with it,
//adding the jsonknowntypes alone conversion results in an exception
//expected: the conversion to succeed, deserializing the base class
var @base = new Base { BaseProp = "base" };
var json = JsonConvert.SerializeObject(@base, Formatting.Indented);
Console.WriteLine(json);
//remove the $type discriminator to simulate the scenario
var token = JToken.Parse(json);
token["$type"].Parent.Remove();
Console.WriteLine(token.ToString(Formatting.Indented));
var deserialized = JsonConvert.DeserializeObject<Base>(token.ToString());
}
[JsonConverter(typeof(JsonKnownTypesConverter<Base2>))]
[JsonKnownTypeFallback(typeof(Base2))]
public class Base2
{
public string BaseProp { get; set; }
}
public class Derived2 : Base2
{
public string DerivedProp { get; set; }
}
[TestMethod]
public void TestLegacyBaseClassDeserialization2()
{
//in the scenario where a base type was serialized in old code, before the jsonknowntype was associated with it,
//adding the jsonknowntypes conversion and a fallback
//attempting to deserialize as the derived type results in an exception
//expected: i'm not sure - perhaps it's perfectly valid to fail
//also, this would be fixed by JsonDiscriminator.UseBaseTypeForCanConvert
var @base = new Base2 { BaseProp = "base2" };
var json = JsonConvert.SerializeObject(@base, Formatting.Indented);
Console.WriteLine(json);
//no $type discriminator is emitted
var deserialized2 = JsonConvert.DeserializeObject<Derived2>(json);
}
}
}
For now don't have time to figure this out, if u can figure this out and fix if needed ill merge and publish that quickly