toong
toong

Reputation: 1380

Deserialize json objects and transform inner objects to a string value?

I have a webservice returning json data. I have no control over the server side generated json.

I'm deserializing json like this:

JsonConvert.DeserializeObject<OuterObject>(jsonString);

The problem is there are inner objects embedded (with a whole lot of more nested inner objects). I have no interest in modeling them in my application.

The json-data goes like this:

{ 
    id : "xyz",
    name : "Some Object",
    properties : {
        prop_1 : "foo",
        prop_2 : "bar"
    },
    inner_object : {
        id : "abc$1",
        name : "Inner Object Name",
        ....
        // a whole lot of stuff here
        // with more embedded objects
        ....
    }
}

I would like to model the outer object as a simple POCO, where the inner-object is referenced by just a (String) id, not an object reference.

public class Outer
{
    public String Id { get; internal set; }
    public String Name { get; internal set; }
    public Dictionary<String,String> Properties { get; internal set; }

    // Just keep the InnerObject Id, no real reference to an instance
    public String InnerObjectId { get; set; }
}

I guess I could write an JsonOuterObject version with a real object reference to JsonInnerObject and construct my real OuterObject from there, throwing away the other objects afterwards ... but that's way too lame. (I don't want to see my name next in a commit like that)

So I'm playing around with Json.NET's IContractResolver (overriding the DefaultContractResolver) now, but it looks like I'm in for a long ride here.

Am I missing some obvious Json.NET feature that takes care of this for me here ?

Or maybe some pointers what methods of IContractResolver are interesting here ?

EDIT: A POJO in .NET is a POCO aparently.

Upvotes: 5

Views: 5418

Answers (2)

toong
toong

Reputation: 1380

Creating a custom JsonConverter for the Outer-type allows you a lot of flexibility when deserializing objects.

It's considerably more work, but it can be worth the effort. Especially if you have no control over the returned JSON object and you would like to model the returned data differently in your client application.

The meat of the JsonConverter implementation is overriding the ReadJson method

public override object ReadJson(JsonReader reader,
                                Type objectType,
                                object existingValue,
                                JsonSerializer serializer)

The JsonReader is a tokenized stream of your data. An implementation could go like this:

public override object ReadJson(JsonReader reader,
                                Type objectType,
                                object existingValue,
                                JsonSerializer serializer)
{
    var outer = new Outer()

    while (reader.TokenType != JsonToken.EndObject)
    {
        if (reader.TokenType == JsonToken.PropertyName)
        {
            var propertyName = reader.Value.ToString();
            reader.Read();

            switch (propertyName)
            {
                case "id":
                    outer.Id = serializer.Deserialize<String>(reader);
                    break;
                case "id":
                    outer.Properties = serializer.Deserialize<Dictionary<String,String>>(reader);
                    break;
                case "inner_object"
                    var inner = serializer.Deserialize<Inner>(reader);
                    outer.InnerObjectId = inner.Id;
                    break;
                [...more cases...]
                default:
                    serializer.Deserialize<object>(reader);
                    break;
                }
                reader.Read(); // consume tokens in reader
            }
        } else {
            // throw exception ?
        }
    }

    return outer;
}

You can annotate your Outer object with the JsonConverterAttribute or pass a long the converter to the (overloaded) Deserialize(String json, params JsonConverter[] converters) method of the JsonConverter class

Upvotes: 3

Frank
Frank

Reputation: 3143

One approach would be to use Newtonsoft.Json.Linq and dynamic types since you are essentially trying to (or being forced to) bend type safety rules.

    public class Outer
    {            
        [JsonProperty(PropertyName = "id")]
        public String Id { get; internal set; }
        [JsonProperty(PropertyName = "name")]
        public String Name { get; internal set; }
        [JsonProperty(PropertyName = "properties")]
        public Dictionary<String, String> Properties { get; internal set; }
        [JsonProperty(PropertyName = "inner_object")]
        public dynamic InnerObjectId { get; set; }
    }

    public void InnerObjectAsDynamic()
    {
        const string json = @"{""id"":""xyz"",""name"":""Some Object"",""properties"":{""prop_1"":""foo"",""prop_2"":""bar""},""inner_object"":{""id"":""abc$1"",""name"":""Inner Object Name""}}";
        var outer = JsonConvert.DeserializeObject<Outer>(json);
        var innerObjectJson = outer.InnerObjectId.ToString();
        Console.WriteLine(innerObjectJson);
        //{
        //  "id": "abc$1",
        //  "name": "Inner Object Name"
        //}
    }

Alternatively, you could define Outer as:

    public class Outer
    {
        [JsonProperty(PropertyName = "id")]
        public String Id { get; internal set; }
        [JsonProperty(PropertyName = "name")]
        public String Name { get; internal set; }
        [JsonProperty(PropertyName = "properties")]
        public Dictionary<String, String> Properties { get; internal set; }
        [JsonProperty(PropertyName = "inner_object")]
        public Newtonsoft.Json.Linq.JObject InnerObjectId { get; set; }
    }

This is a little cleaner to me. Best of luck.

Upvotes: 3

Related Questions