Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;

var json = JsonConvert.SerializeObject(expando);
public class JsonController : Controller
{
    public ActionResult Data()
    {
        dynamic expando = new ExpandoObject();
        expando.name = "John Smith";
        expando.age = 30;

        var json = JsonConvert.SerializeObject(expando);

        return Content(json, "application/json");
    }
}
{"name":"John Smith","age":30}

In the context of an ASP.NET MVC Controller, the result can be returned using the Content-method:

Using JSON.NET you can call SerializeObject to "flatten" the expando object:

newtonsoft.json has better handling for recursive expandos inside expandos or dictionaries and inner dictionaries, out of the box

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


"{SomeProp: SomeValueOrClass}"
public static string Flatten(this ExpandoObject expando)
{
    StringBuilder sb = new StringBuilder();
    List<string> contents = new List<string>();
    var d = expando as IDictionary<string, object>;
    sb.Append("{");

    foreach (KeyValuePair<string, object> kvp in d) {
        contents.Add(String.Format("{0}: {1}", kvp.Key,
           JsonConvert.SerializeObject(kvp.Value)));
    }
    sb.Append(String.Join(",", contents.ToArray()));

    sb.Append("}");

    return sb.ToString();
}
return JsonResult(expando.Flatten());
var obj = JSON.parse(myJsonString);

-1: Doing this in an extension method that returns a string is not the correct way to interface this behavior with the framework. You should extend the built in serialization architecture instead.

And I can use it in javascript by doing this (referenced here):

Don't eval it! You should use a JSON deserializer to avoid security issues. See json2.js: json.org/js.html var o = JSON.parse(myJsonString);

I solved this by writing an extension method that converts the ExpandoObject into a JSON string:

Major drawback of this method is a lack of recursion - if you know the top-level object to be dynamic and that's it, this works, but if dynamic objects could be at any or every level of the object tree returned, this fails.

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


public string Flatten(ExpandoObject expando)
    {
        StringBuilder sb = new StringBuilder();
        List<string> contents = new List<string>();
        var d = expando as IDictionary<string, object>;
        sb.Append("{ ");

        foreach (KeyValuePair<string, object> kvp in d)
        {       
            if (kvp.Value is ExpandoObject)
            {
                ExpandoObject expandoValue = (ExpandoObject)kvp.Value;
                StringBuilder expandoBuilder = new StringBuilder();
                expandoBuilder.Append(String.Format("\"{0}\":[", kvp.Key));

                String flat = Flatten(expandoValue);
                expandoBuilder.Append(flat);

                string expandoResult = expandoBuilder.ToString();
                // expandoResult = expandoResult.Remove(expandoResult.Length - 1);
                expandoResult += "]";
                contents.Add(expandoResult);
            }
            else if (kvp.Value is List<Object>)
            {
                List<Object> valueList = (List<Object>)kvp.Value;

                StringBuilder listBuilder = new StringBuilder();
                listBuilder.Append(String.Format("\"{0}\":[", kvp.Key));
                foreach (Object item in valueList)
                {
                    if (item is ExpandoObject)
                    {
                        String flat = Flatten(item as ExpandoObject);
                        listBuilder.Append(flat + ",");
                    }
                }

                string listResult = listBuilder.ToString();
                listResult = listResult.Remove(listResult.Length - 1);
                listResult += "]";
                contents.Add(listResult);

            }
            else
            { 
                contents.Add(String.Format("\"{0}\": {1}", kvp.Key,
                   JsonSerializer.Serialize(kvp.Value)));
            }
            //contents.Add("type: " + valueType);
        }
        sb.Append(String.Join(",", contents.ToArray()));

        sb.Append("}");

        return sb.ToString();
    }

I took the flattening process one step further and checked for list objects, which removes the key value nonsense. :)

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


dynamic expando = new ExpandoObject();
expando.Blah = 42;
expando.Foo = "test";
...

var d = expando as IDictionary<string, object>;
d.Add("SomeProp", SomeValueOrClass);

// After you've added the properties you would like.
d = d.ToDictionary(x => x.Key, x => x.Value);
return new JsonResult(d);

"expando.Add" does not work for me. I believe in this case it is "d.Add" (that worked for me).

An ExpandoObject gives you much more flexibility than a simple Dictionary. Although the example above does not demonstrate it, you can use the dynamic features of the ExpandoObject to add the properties that you want to have in your JSON. A normal Dictioanry object will convert to JSON without any problems, so by doing the conversion, it's a simple O(n) way to put the easy-to-use dynamic ExpandoObject in to a format that can be JSONified. You are correct though, the example above would be a rediculus use of the ExpandoObject; a simple Dictionary would be much better.

Here's what I did to achieve the behavior you're describing:

Like that approach more - creating a copy doesn't work in any environment but I have only small objects and the Expando is provided by an (unchangeable) 3rd party....

Nice. You can also cast the dynamic on the fly: return new JsonResult(((ExpandoObject)someIncomingDynamicExpando).ToDictionary(item => item.Key, item => item.Value))

So wait... you're creating an ExpandoObject, casting it as a dictionary, using it like a dictionary, and then when that's not good enough, converting it to a dictionary... ... why not just use a dictionary in this case? ... o_o

The cost is that you're making a copy of the data before serializing it.

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


using System.Web.Script.Serialization;    
public class ExpandoJSONConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {         
        var result = new Dictionary<string, object>();
        var dictionary = obj as IDictionary<string, object>;
        foreach (var item in dictionary)
            result.Add(item.Key, item.Value);
        return result;
    }
    public override IEnumerable<Type> SupportedTypes
    {
        get 
        { 
              return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) });
        }
    }
}
var serializer = new JavaScriptSerializer(); 
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()});
var json = serializer.Serialize(obj);

Great work @pablo.Excellent example of plugging a custom serialization routine into the MVC framework!

My serializer gets called recursively. If I set RecursionLimit, I either get recursion limit exceeded error or stack overflow exception error. What should I do? :(

The easiest way that I found to do this was: new JavaScriptSerializer().Deserialize<object>(Newtonsoft.Json.JsonConvert.SerializeObject(listOfExpandoObject)); what do you think?

This worked great for my needs. If anyone wants to plug in some code for the NotImplementedException to add something like serializer.Deserialize<ExpandoObject>(json);, @theburningmonk offers a solution that worked for me.

You could also, make a special JSONConverter that works only for ExpandoObject and then register it in an instance of JavaScriptSerializer. This way you could serialize arrays of expando,combinations of expando objects and ... until you find another kind of object that is not getting serialized correctly("the way u want"), then you make another Converter, or add another type to this one. Hope this helps.

thanks a lot . small and useful code

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


Answer moved from this question on request

I changed the name of the dictionary to "Fields" and then this serializes with Json.Net to produce json which looks like:

It would be perfect if I could get rid of the extra "Fields" property which encapsulates the rest, but I've worked around that limitation.

This may not be useful to you, but I had a similar requirement, but used a SerializableDynamicObject

{"Fields":{"Property1":"Value1", "Property2":"Value2" etc. where Property1 and Property2 are Dynamically added properties - i.e. Dictionary Keys

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


Using returning dynamic ExpandoObject from WebApi in ASP.Net 4, the default JSON formatter seems to flatten ExpandoObjects into simple JSON object.

Note
Rectangle 27 0

c How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?


dynamic person = new System.Dynamic.ExpandoObject();
        person.FirstName  = "John";
        person.LastName   = "Doe";
        person.Address    = "1234 Home St";
        person.City       = "Home Town";
        person.State      = "CA";
        person.Zip        = "12345";

        var writer = new JsonFx.Json.JsonWriter();
        return writer.Write(person);

You can also do this using JSON .Net (Newtonsoft) by completing the following steps. var entity = person as object; var json = JsonConvert.SerializeObject(entity);

Note