Based on the examples posted here on StackOverflow and in the Microsoft documentation, I tried to get polymorphic deserialization to work.
The example in the accepted answer given on the StackOverflow post works perfectly, as long as the object list you're trying to (De)Serialize is of the type List<BaseClass>
.
I now need to (De)Serialize a list of DerivedA
classes, which throws the error
InvalidCastException: Unable to cast object of type 'BaseClassConverter' to type 'System.Text.Json.Serialization.JsonConverter`1[DerivedA]'.
I think I understand this is because of the lack of covariance in the JsonConverter generic implementation.
Unfortunately, I do not understand nor did I find a possible work around or implementation to get my situation to work.
In case something happens with the answer I've linked to, this is the code I'm trying to get working:
var objects = new List<DerivedA> { new DerivedA(), new DerivedA() };
// Using: System.Text.Json
var options = new JsonSerializerOptions
{
Converters = { new BaseClassConverter() },
WriteIndented = true
};
string jsonString = JsonSerializer.Serialize(objects, options);
Console.WriteLine(jsonString);
public class BaseClass
{
public int Int { get; set; }
}
public class DerivedA : BaseClass
{
public string Str { get; set; }
}
public class DerivedB : BaseClass
{
public bool Bool { get; set; }
}
public class BaseClassConverter : JsonConverter<BaseClass>
{
private enum TypeDiscriminator
{
BaseClass = 0,
DerivedA = 1,
DerivedB = 2
}
public override bool CanConvert(Type type)
{
return typeof(BaseClass).IsAssignableFrom(type);
}
public override BaseClass Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
if (!reader.Read()
|| reader.TokenType != JsonTokenType.PropertyName
|| reader.GetString() != "TypeDiscriminator")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.Number)
{
throw new JsonException();
}
BaseClass baseClass;
TypeDiscriminator typeDiscriminator = (TypeDiscriminator)reader.GetInt32();
switch (typeDiscriminator)
{
case TypeDiscriminator.DerivedA:
if (!reader.Read() || reader.GetString() != "TypeValue")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
baseClass = (DerivedA)JsonSerializer.Deserialize(ref reader, typeof(DerivedA));
break;
case TypeDiscriminator.DerivedB:
if (!reader.Read() || reader.GetString() != "TypeValue")
{
throw new JsonException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
baseClass = (DerivedB)JsonSerializer.Deserialize(ref reader, typeof(DerivedB));
break;
default:
throw new NotSupportedException();
}
if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject)
{
throw new JsonException();
}
return baseClass;
}
public override void Write(
Utf8JsonWriter writer,
BaseClass value,
JsonSerializerOptions options)
{
writer.WriteStartObject();
if (value is DerivedA derivedA)
{
writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedA);
writer.WritePropertyName("TypeValue");
JsonSerializer.Serialize(writer, derivedA);
}
else if (value is DerivedB derivedB)
{
writer.WriteNumber("TypeDiscriminator", (int)TypeDiscriminator.DerivedB);
writer.WritePropertyName("TypeValue");
JsonSerializer.Serialize(writer, derivedB);
}
else
{
throw new NotSupportedException();
}
writer.WriteEndObject();
}
}