19 Jul 2010

Turning JSON into a ExpandoObject

20 Comments Uncategorized

Recently I had the need for a web service of mine to take a JSON blob as an input.  This isn’t really exciting or all that interesting a problem, but I really didn’t enjoy the code smell that came from drilling in to the resulting Dictionary object that comes from desterilizing the JSON object into something that .NET understands.

public ActionResult Create()
{
    IDictionary<string, object> request;

    using (var bodyStream = new StreamReader(Request.InputStream))
    {
        var json = bodyStream.ReadToEnd();

        JavaScriptSerializer ser = new JavaScriptSerializer();
        request = ser.Deserialize<IDictionary<string, object>>(json);
    }

    var accountRequest = request["account"] as IDictionary<string, object>;
    var billingRequest = request["billing"] as IDictionary<string, object>;
    var billingInfoRequest = billingRequest["info"] as IDictionary<string, object>;
    var billingInvoiceRequest = billingRequest["invoice"] as IDictionary<string, object>;
    var billingItemsRequest = billingRequest["items"] as IDictionary<string, object>;

    // create account
    var account = new Account {
        CreatedOn = DateTime.UtcNow,
        Email = accountRequest["email"] as string,
        Name = accountRequest["company"] as string,
        UserName = request["user_name"] as string
    };

    // ... more code using the dictionary object
}

After remembering that the ExpandoObject was also based on IDictionary<string, object> I thought it might be a marriage met in heaven.

The ExpandoObject class enables you to add and delete members of its instances at run time and also to set and get values of these members. This class supports dynamic binding, which enables you to use standard syntax likesampleObject.sampleMember instead of more complex syntax like sampleObject["sampleMember"].

Since I won’t know what the JSON will actually look like until it is passed in to the web service, and I really don’t want to create different objects just for the sake of making a temp pass through object from JSON to my app code, the use of the ExpandoObject is great because it helps keep my code clean looking and more readable.  Because as a developer I don’t really care that the data is coming in as JSON, all that I really care about is the data it self.  So my aim is to simplify my life and the readability of my code.

The first thing I usually do when creating an API is write a reference application, or in other words use a test driven approach when starting to write a new API.  I started with the following reference code:

public ActionResult Create()
{
    dynamic request;    

    using (var bodyStream = new StreamReader(Request.InputStream))
    {
        var json = bodyStream.ReadToEnd();

        JavaScriptSerializer ser = new JavaScriptSerializer();
        var dictionary = ser.Deserialize<IDictionary<string, object>>(json);
        var request = dictionary.Expando();
    }

    var account = new Account {
        CreatedOn = DateTime.UtcNow,
        Email = request.account.email,
        Name = request.account.company,
        UserName = request.account.user_name
    };

    // ... more code using the expando object
}

This seems much cleaner doesn’t it?  Now let me so you the Expando extension method that I created to accomplish this.  It is relatively straight forward I take an IDictionary<string, object> object and copy it into an ExpandoObject object, which is also implements IDictionary<string, object>.  So the copying from one to the other is pretty straight forward for the most part.

public static ExpandoObject Expando(this IDictionary<string, object> dictionary)
{
    var expando = new ExpandoObject();
    var expandoDic = (IDictionary<string, object>)expando;

    foreach (var item in dictionary)
    {
        bool alreadyProcessed = false;

        if (item.Value is IDictionary<string, object>)
        {
            expandoDic.Add(item.Key, Expando((IDictionary<string, object>)item.Value));
            alreadyProcessed = true;
        }
        else if (item.Value is ICollection)
        {
            var itemList = new List<object>();
            foreach (var item2 in (ICollection)item.Value)
                if (item2 is IDictionary<string, object>)
                    itemList.Add(Expando((IDictionary<string, object>)item2));
                else
                    itemList.Add(Expando(new Dictionary<string, object> { { "Unknown", item2 } }));

            if (itemList.Count > 0)
            {
                expandoDic.Add(item.Key, itemList);
                alreadyProcessed = true;
            }
        }

        if (!alreadyProcessed)
            expandoDic.Add(item);
    }

    return expando;
}

Not as straight forward as a foreach loop, but not totally out of the question for something that you couldn’t read and understand in about 10 minutes.

This may add a slight bit of overhead between the coping of one dictionary into another, and the use of the dynamic runtime, but I think the over all ease in readability is more of a productivity gain than the slight bit of overhead that was gained.

Tags: , ,
written by
Nick Berardi
subscribe
If you found this post valuable and would like to see more like it you can follow me.

20 Responses to “Turning JSON into a ExpandoObject”

  1. Reply Hugoware says:

    Cool stuff – It is really interesting to see what ‘dynamic’ is going to allow us to do with our code. I recently did something similar but using anonymous types to create and modify dynamic types. (if interested – http://somewebguy.wordpress.com/2010/07/15/almost-sorta-real-dynamic-in-net/)

  2. Reply Fred says:

    Do we forget about continue; ?

  3. Reply cautionsign says:

    Can we do reflection for the ExpandoObject? Seems I cannot reflect those properties and methods I dynamically created. Thanks.

  4. Reply Nick Berardi says:

    Dynamic has to be accessed differently from reflection. But alternatively in the case of expando you can just cast to dictionary. And loop through much faster than reflection.

  5. Reply woaksie says:

    Now all we need is a method to convert an ExpandoObject back to Json…

    • Reply Nick Berardi says:

      The built in JavascriptSerializer will do it. Because ExpandoObject impliments IDictionary which is the basis of how the JavascriptSerializer converts between JSON and objects.

  6. Reply woaksie says:

    I tried that and found that when this json

    {“account”:{“email”:”john.doe@yahoo.com”,”company”:”Example Corp”,”user_name”:”john.doe”},”sue”:3}

    converted to the ExpandoObject and then converted back to json gave this:

    [{"Key":"account","Value":[{"Key":"email","Value":"john.doe@yahoo.com"},{"Key":"company","Value":"Example Corp"},{"Key":"user_name","Value":"john.doe"}]},{“Key”:”sue”,”Value”:3}]

    And if I tried to convert this back to an ExpandoObject if failed.

    • Reply Nick Berardi says:

      Hi Woaksie, What method did you use to convert it back to JSON? Can you reply with the code? Because something is wrong, because it broke out every key/value pair in to a new property.

  7. Reply woaksie says:

    public static string ToJson(this ExpandoObject obj)
    {
    var serializer = new JavaScriptSerializer();
    return serializer.Serializer(obj);
    }

    and then call

    var json = ((ExpandoObject)expando).ToJson();

    • Reply Nick Berardi says:

      HI woaksie,

      Since ExpandoObject also implements IEnumerable> you need to specify the that you want to serialize it using IDictionary. To do this with the built in JavaScript Serializer I usually do the following:

      public static string ToJson(this ExpandoObject obj)
      {
      var serializer = new JavaScriptSerializer();
      var dictionary = new Dictionary(obj);
      return serializer.Serializer(dictionary);
      }

      Hope this helps.

  8. Reply woaksie says:

    Thanks for the quick responses. I’ll give that a go.

  9. Reply David Taylor says:

    Hi,

    Feel free to email me if you can think of a better way, but I do think we need a version that converts ExpandoObject back to JSON. The solution you outlined above simply will not work with a hierarchy of ExpandoObjects (i.e. with an object graph).

    Sure…the first level will be correctly serialized but everything else will be serialized as IEnumerable instead of IDictionary and you will get Key:, Value:…etc in the JSON.

    I am half way through writing the equivalent code – so if you still don’t think it is needed please send me an email and tell me to stop :-)

    Thanks, David

  10. Reply David Taylor says:

    Hey Nick,

    Thanks for the response but you are going to laugh at this – we are going about it the wrong way!

    The javascript serializer lets you register a type converter for both serialization and deserialization – I was only interested in serialization. So in a few lines of code I was able to write one of these that just converts the ExpandoObject into a real dictionary and it now works perfectly. It works over the entire object graph, so no need for me to write recursive code, etc. You use it like this:

    var serializer = new JavaScriptSerializer();
    serializer.RegisterConverters(new[] { new ExpandoConverter() });
    …then use the serializer.

    Check out the MSDN doco for: System.Web.Script.Serialization.JavaScriptConverter

    Thanks.

Leave a Reply