TL;DR: Is there an easy way in json.net to inspect the type of a property and create an instance based on that?
I have the following two objects in JSON, these are relation objects in the JSON API:
{ "data": { "type": "Test", "id": "1" } }
and
{ "data": [{ "type": "Test", "id": "1" }, { "type": "Test", "id": "2" }]}
Using json.net, I want to serialize these objects to either the class ToOneRelation, or ToManyRelation. The classes look like this:
[JsonConverter(typeof(RelationshipConverter))]
abstract class Relation
{
[JsonProperty("meta")]
public Dictionary<string, object> Meta { get; set; }
}
class ToOneRelation : Relation
{
[JsonProperty("data")]
public object Data { get; set; }
}
class ToManyRelation : Relation
{
[JsonProperty("data")]
public List<object> Data { get; set; }
}
Generally I only know that I want to have a Relation, so I would do the following:
var result = JsonConvert.DeserializeObject<Relation>(json);
In order to instantiate the correct object, I need a custom converter, where I check the type of the "data" property. When the type is an array, I use the ToManyRelation, otherwise I use the ToOneRelation.
Currently I created a custom converter that will walk over all properties and deserialize them.
object toOneData = null;
List<object> toManyRelationData = null;
Dictionary<string, object> meta = null;
reader.Read();
while (reader.TokenType == JsonToken.PropertyName)
{
string propertyName = reader.Value.ToString();
reader.Read();
if (string.Equals(propertyName, "data", StringComparison.OrdinalIgnoreCase))
{
if (reader.TokenType == JsonToken.StartObject)
{
toOneData = serializer.Deserialize<object>(reader);
}
else if (reader.TokenType == JsonToken.StartArray)
{
toManyRelationData = serializer.Deserialize<List<object>>(reader);
}
}
else if (string.Equals(propertyName, "meta", StringComparison.OrdinalIgnoreCase))
{
meta = serializer.Deserialize<Dictionary<string, object>>(reader);
}
else
{
reader.Skip();
}
reader.Read();
}
Although this works, I have the problem that this code isn't really maintainable. As soon as an extra property is introduced to the Relation object (or the other objects) I need to modify this code to reflect those changes.
So my question is is, is there a better way to inspect a property to identify which type to create and than use serializer.Populate
method to do the rest?
(The CustomCreationConverter doesn't work, because it can't inspect the properties before creating the object is created...)
(Note, a working example can be found here)