Matt Burland
Matt Burland

Reputation: 45155

Deserializing a nested object in JSON to a specific type

I have a class on the C# end that looks something like this:

[DataContract]
public class MyObject
{
    [DataMember]
    public SomeEnum FooType { get; set; }

    [DataMember]
    public FooBase MyFoo { get; set; }
}

Where, basically, the value in the property FooType should tell you what specific type derived from FooBase is present in the property MyFoo.

Now if I just wanted to deserialize a object derived from FooBase I could just do something like:

var myFoo = JsonConvert.DeserializeObject(myJsonString, typeof(FooDerived)) as FooDerived;

But how do I deserialize a MyObject where the FooBase object is nested and the information about what type it is can only be determined by partially deserializing the object first?

I'm thinking this is going to need a custom converter derived from JsonConverter, but I'm not entirely sure how to make ReadJson work here.

Something like this?

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var result = new MyObject();
    while(reader.Read())
    {
        if(reader.TokenType == JsonToken.PropertyName)
        {
           var prop = reader.Value as string;
           if (prop == "FooType")
           {
               reader.Read();
               result.FooType = (SomeEnum)reader.ReadAsInt32();   // or something like that
           }
           if (prop == "MyFoo")
           {
               reader.Read();
               // now the reader.TokenType should be StartObject, but I can't
               // deserialize the object because I don't know what type it is
               // I might not have read "FooType" yet 
               // So I really need to pull this whole sub object out as a string
               // and deserialize it later???
           }
        }
    }
    return result;
}

Upvotes: 2

Views: 2799

Answers (2)

Matt Burland
Matt Burland

Reputation: 45155

Using this answer as inspiration: https://stackoverflow.com/a/19308474/1250301

I came up with something like this:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var jObj = JObject.Load(reader);
    var foo = jObj["MyFoo"];
    var result = new MyObject(); 
    result.FooType = jObj["FooType"].ToObject<SomeEnum>();
    switch (result.FooType)
    {
        case SomeEnum.Value1:
            result.MyFoo = foo.ToObject<FooType1>();
            break;
        case SomeEnum.Value2:
            result.MyFoo = foo.ToObject<FooType2>();
            break;
        case SomeEnum.Value3:
            result.MyFoo = foo.ToObject<FooType3>();
            break;
        default:
            throw new Exception("Unknown FooType");
    }
    return result;
}

The only problem here is that if I add new properties to the parent object, I'm going to need to manually map them. I thought I might be able to just do something like:

var parent = jObj.ToObject<MyObject>(); 

And then fill in the MyFoo object, but this will just end up calling ReadJson again.

Upvotes: 3

aaronmallen
aaronmallen

Reputation: 1438

I believe you can use Json.Linq to accomplish this. I am not in front of a computer to test this but I believe its something like:

string fooTypeJson = JObject.Parse(myJsonString).SelectToken("FooType").ToString();
FooType fooType = reader.DeserializeObject<FooType>(fooTypeJson);

Upvotes: 2

Related Questions