Usually, for derived types deserialization, you have to define a custom converter. I mean when there is a base class - Base and others derived from the Base. For that case you have to define a custom convevrter and use some data (maybe an enum) from each derived class to know to which type to convert the data. The problem is when this convertor is called for reference values, for which representation looks like {"$ref":"1"}
and the convertor can't obtain any info from this.
Any ideas how to handle this?
Here is the source for better understanding:
public enum ElementType
{
Invalid = 0,
FirstElement,
SecondElement
}
public class SubElement
{
[JsonConstructor]
private SubElement() { }
public SubElement(string name, Element parent)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("message", nameof(name));
}
Name = name;
Parent = parent ?? throw new ArgumentNullException(nameof(parent));
}
[JsonProperty]
public string Name { get; private set; }
[JsonProperty]
public Element Parent { get; private set; }
}
[JsonObject(IsReference = true)]
[JsonConverter(typeof(BaseConverter))]
public abstract class Element
{
[JsonConstructor]
protected Element() { }
public Element(string name, IList<SubElement> subelements)
{
Name = name;
Subelements = subelements;
}
[JsonProperty]
public string Name { get; private set; }
[JsonProperty]
public IList<SubElement> Subelements { get; private set; }
public abstract ElementType Type { get; }
}
public class FirstElement : Element
{
[JsonConstructor]
private FirstElement() { }
public FirstElement(string name, IList<SubElement> subelements) : base(name, subelements) { }
public override ElementType Type => ElementType.FirstElement;
}
public class BaseConverter : CustomCreationConverter<Element>
{
private ElementType elementType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = JObject.ReadFrom(reader);
elementType = obj["Type"].ToObject<ElementType>();
return base.ReadJson(obj.CreateReader(), objectType, existingValue, serializer);
}
public override Element Create(Type objectType)
{
switch (elementType)
{
case ElementType.FirstElement:
return Activator.CreateInstance(typeof(FirstElement), true) as Element;
default:
throw new NotImplementedException();
}
}
}
class Program
{
static void Main(string[] args)
{
Element firstElement = new FirstElement("FirstElement", new List<SubElement>());
firstElement.Subelements.Add(new SubElement("Subelement1", firstElement));
firstElement.Subelements.Add(new SubElement("Subelement2", firstElement));
string serialized = JsonConvert.SerializeObject(firstElement);
Console.WriteLine(serialized);
Element deserialized = JsonConvert.DeserializeObject<Element>(serialized);
}
}
Serialization output:
{
"$id": "1",
"Type": 1,
"Name": "FirstElement",
"Subelements": [
{
"Name": "Subelement1",
"Parent": {
"$ref": "1"
}
},
{
"Name": "Subelement2",
"Parent": {
"$ref": "1"
}
}
]
}