Shawn Weisfeld

I find when I talk to myself nobody listens. - Shawn Weisfeld
posts - 356, comments - 173, trackbacks - 34

My Links

News

The views expressed in this blog are mine and mine alone, not that of my employer, Microsoft, or anyone else’s. No warrantee is given for the quality of any material on this site.

Archives

Post Categories

Using C# 4.0 and dynamic to parse JSON

 

UPDATE: take a look at http://json.codeplex.com/ before you try and roll your own, it supports dynamic now...

 

I recently had to get a JSON feed from the web and traverse it. Like any good developer I started off with a Bing search and stumbled across a few posts.

 

The first by Nikhil Kothari looked interesting but his implementation did way too much, all I needed to do was read a JSON file, did I really need all that code.

http://www.nikhilk.net/CSharp-Dynamic-Programming-JSON.aspx

 

The second post I saw by Alex Ghiondea again looked interesting but relied on a library from codeplex (JSON.NET, http://json.codeplex.com/) and again I thought this was a bit overkill for my project.

http://blogs.msdn.com/b/alexghi/archive/2008/12/22/using-anonymous-types-to-deserialize-json-data.aspx

 

But Nikhil’s post did get me thinking that this might be a good place to use the new dynamic “type” in C# 4.0. With some further research I dug up that in .NET 3.5 we got a new class called the JavaScriptSerializer, MSDN describes it as:

The JavaScriptSerializer class is used internally by the asynchronous communication layer to serialize and deserialize the data that is passed between the browser and the Web server. You cannot access that instance of the serializer. However, this class exposes a public API. Therefore, you can use the class when you want to work with JavaScript Object Notation (JSON) in managed code.

http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer(v=VS.100).aspx

 

To use the JavaScriptSerializer (from the System.Web.Extensions dll) you have to implement a JavaScriptConverter. I can do that. :) After poking around a bit with the JavaScriptSerializer I realized that it gives you a few things. A value, an array list of values, a dictionary of key value pairs, or an ArrayList containing an array of dictionary of key value pairs. What I want to do is if I see a dictionary I want my user to be able to use the key as a property on my dynamic, if I see an ArrayList I want the user to be able to iterate over the collection and see its members, finally if I see a value I want to just return it.

Lets start with a simple JSON file, that I borrowed from json.org:

{
    "glossary": {
        "title": "example glossary",
        "GlossDiv": {
            "title": "S",
            "GlossList": {
                "GlossEntry": {
                    "ID": "SGML",
                    "SortAs": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "Acronym": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "GlossDef": {
                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
                        "GlossSeeAlso": ["GML", "XML"]
                    },
                    "GlossSee": "markup"
                }
            }
        }
    }
}

http://www.json.org/example.html

 

So with this the above JSON I want to be able to write “glossaryEntry.glossary.title” or “glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.ID” and get back “example glossary” and “SGML” respectively. All without having to actually create a static “glossary” object.

So the first step is to create our DynamicObject. I called mine DynamicJsonObject and as you can see it inherits from the base DynamicObject class.

 

public class DynamicJsonObject : DynamicObject
{
    private IDictionary<string, object> Dictionary { get; set; }

    public DynamicJsonObject(IDictionary<string, object> dictionary)
    {
        this.Dictionary = dictionary;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = this.Dictionary[binder.Name];

        if (result is IDictionary<string, object>)
        {
            result = new DynamicJsonObject(result as IDictionary<string, object>);
        }
        else if (result is ArrayList && (result as ArrayList) is IDictionary<string, object>)
        {
            result = new List<DynamicJsonObject>((result as ArrayList).ToArray().Select(x => new DynamicJsonObject(x as IDictionary<string, object>)));
        }
        else if (result is ArrayList)
        {
            result = new List<object>((result as ArrayList).ToArray());
        }

        return this.Dictionary.ContainsKey(binder.Name);
    }
}

The tricky bit is in the “TryGetMember” Method. The binder.Name is the property value you used in your code. As you can see I am looking that guy up in the current internal dictionary. I then test its type. If it is a dictionary, I wrap it in another DynamicJsonObject and give it back. If it is an ArrayList of Dictionaries then I wrap each item as an DynamicJsonObject and send back the List. If it is just an Array List I convert it to a List and send it back. Finally we have to tell .NET if we found it at all.

The only other thing we need is an implementation of the JavaScriptConverter.

    public class DynamicJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            if (type == typeof(object))
            {
                return new DynamicJsonObject(dictionary);
            }

            return null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(object) })); }
        }
    }

As you can see by the above code we are only supporting the deserialize method, and the only thing we do is wrap the dictionary with our DynamicJsonObject. Finally since we are using dynamic we can support any object type with this converter.

Now for the fun bit, lets use it.

            JavaScriptSerializer jss = new JavaScriptSerializer();
            jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });

            dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;

            Console.WriteLine("glossaryEntry.glossary.title: " + glossaryEntry.glossary.title);
            Console.WriteLine("glossaryEntry.glossary.GlossDiv.title: " + glossaryEntry.glossary.GlossDiv.title);
            Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.ID: " + glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.ID);
            Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.para: " + glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.para);
            foreach (var also in glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso)
            {
                Console.WriteLine("glossaryEntry.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso: " + also);
            }

To use our converter we create a serializer, register our converter with it and then call the deserialize method. Now we can use our standard “dot” notation to traverse our JSON object without the need for external libraries and in under 100 lines of code. Now JSON in .NET feels a lot like JSON in JavaScript. . . When I heard that C# was getting this dynamic stuff I was unsure of its power and relevance, now I have “seen the light” and can really see where this was a great addition to the language.

Print | posted on Sunday, August 22, 2010 8:03 AM | Filed Under [ C# ]

Feedback

Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Thanks for posting this code, however there is a bug. I posted a version that includes the fix here:

stackoverflow.com/.../3806407

The offending line is:

if (result is ArrayList && (result as ArrayList) is IDictionary<string, object>)

It's impossible for the result to be both an ArrayList and IDictionary<string,object>.
9/28/2010 12:04 PM | Drew Noakes
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

This is perfect.. Did exactly what I needed to consume a json feed...

Great Post!

Thanks for your help...
11/17/2010 10:35 AM | RC
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Excellent, works like a charm!
I have to get data from one web service in Json and use it to get another set of data from another web service.
This helped me to do everything in C#, MVC instead of going back to Javascript.
Fantastic piece of code.
I just cut and paste and added references and it works.
The bug fix by Drew also really helped.
11/20/2010 6:53 PM | Vijay
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Could you possibly help clarify what exact references and "using" statements need to be added?

Apologies, I'm a complete noob!

For references I added system.web.extensions

using statements added:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Collections;
using System.Collections.ObjectModel;
using System.Web.UI.WebControls;
12/25/2010 9:28 AM | Adam T
Gravatar

# Updated to handle multiple levels of json object

else if (result is ArrayList)
{
var list = new List<object>();
foreach(var o in (result as ArrayList).ToArray())
{
if (o is IDictionary<string, object>)
{
list.Add(new DynamicJsonObject(o as IDictionary<string, object>));
}
else
{
list.Add(o);
}
}
result = list;
}
12/29/2010 2:38 PM | Yuriksan
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

This is exactly what I need, but I'm having a problem...

The line:

dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;

I receive an error: "No overload method for "deserialize" takes 2 arguments"

Any idea?
3/11/2011 4:38 PM | Dave
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

I get an error on dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;

that no overload for method with 2 arguments... is it wrong system.web.extensions.dll or what??
6/18/2011 3:25 PM | Tomas
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Your code have now found its way into https://github.com/robconery/Manatee
:-)
7/12/2011 3:21 PM | Vegar
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

I want use your code but the first i need get data json from response i have referenced link nikescar-textcube.blogspot.com/.../...ty-pack.html and i do
I have post following my code:

HtmlWeb htmlweb = new HtmlWeb();
NameValueCollection postData = new NameValueCollection();
postData.Add("__aa","14");
postData.Add("__pIndex","1");
postData.Add("__pSize","20");
postData.Add("__date","");
postData.Add("__catID", "21");

Now i want get data that response when debug by firebug

{ "TotalItem":168, "TotalPage":9, "d": [{"ArticleId": 6729,"Title": "205: Cấp Giấy chứng nhận Th&#224;nh vi&#234;n lưu k&#253;","Href": "/6729p4c22/205-cap-giay-chung-nhan-thanh-vien-luu-ky.htm","Date": "15/07/2011"},{"ArticleId": 6724,"Title": "015: Đăng k&#253; bổ sung Danh s&#225;ch ban l&#227;nh đạo phụ tr&#225;ch hoạt động lưu k&#253;","Href": "/6724p4c22/015-dang-ky-bo-sung-danh-sach-ban-lanh-dao-phu-trach-hoat-dong-luu-ky.htm","Date": "15/07/2011"}]}
can i do ?
HtmlAgilityPack.HtmlDocument document = htmlweb.SubmitFormValues(postData, "http://vsd.vn/Ajax/action.ashx");
7/20/2011 4:52 AM | huong
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

in this code dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;
is json a file
7/20/2011 10:57 PM | huong
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

if i have json data following
{ "TotalItem":2, "TotalPage":1, "d": [{"ArticleId": 5138,"Title": "017: Thay đổi Th&#224;nh vi&#234;n Ban l&#227;nh đạo phụ tr&#225;ch hoạt động lưu k&#253;","Href": "/5138p4c22/017-thay-doi-thanh-vien-ban-lanh-dao-phu-trach-hoat-dong-luu-ky.htm","Date": "23/03/2011"},{"ArticleId": 5135,"Title": "094: Cấp Giấy chứng nhận Th&#224;nh vi&#234;n lưu k&#253; sửa đổi","Href": "/5135p4c22/094-cap-giay-chung-nhan-thanh-vien-luu-ky-sua-doi.htm","Date": "23/03/2011"}]}

how to i can access?
7/21/2011 10:53 PM | huong
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Cool. I'll try the code out tonight. Thanks for sharing!!!!
8/10/2011 4:15 PM | Newt
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Nice. I actually wrote a JSON parser that returns dynamic types since JavaScriptSerializer is not supported in Silverlight. You can read more here: procbits.com/.../fridaythe13th-the-best-json-pa...
8/17/2011 10:58 AM | JP
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Hi,

I'm kind of lost on the dynamic object. In case of a json like:

{"item1" : "value1",
"item2" : "value2"
}

Assuming that I don't know the key names, how would I access those items?

Thans in advance,

Eric
10/12/2011 11:57 AM | Eric
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

That is one of the drawbacks of the dynamic keyword. You do not get intellisense till the 2nd time you use a property.
10/17/2011 8:45 AM | shawnweisfeld
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Nice post, Shawn :-) After a bit of stubborness on my part, I decided to borrow this, so thanks! I noticed it's used here too :-) github.com/.../JsonHelper.cs and the comment credits you. Very cool.
10/17/2011 7:31 PM | Cori
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

This is exactly what I was searching for, and I asked a question regarding how to deserialize a complex JSON object in StackOverflow, and somebody made a link to this article. Thank you very much. This was really helpful.

11/22/2011 6:56 AM | Saeed Neamati
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

This is amazing thanks man! Got it up and running in no time! It really saved me a hell of a lot of time.
12/30/2011 3:55 AM | Brad
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Hi Adam - you can use the following name spaces...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Collections;
using System.Web.Script.Serialization;
using System.Collections.ObjectModel;

1/3/2012 2:53 PM | George Philip Elavatta
Gravatar

# C# 4.0 and dynamic to parse JSON

This helps cleanup the string-based programming mess that is a characteristic of late-bound code. In fact, there are a number of scenarios that would benefit from dynamic typing in my opinion in addition to interop with other dynamic code.I have been told, that support for indexing into dynamic types already exists in later builds. Thanks for sharing.Regards,
1/30/2012 4:43 AM | Chandler Real Estate
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

It worked very well thank you very nice in a Document
3/20/2012 10:03 PM | oyun
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

I made this into an extension method, thought I would share

var myShinyNewObject = @"{""name"":""Awesome""}".ToDynamic();
Console.Write(myShinyNewObject.name);



public static dynamic ToDynamic(this string pInput)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });

dynamic myDynamic = jss.Deserialize(pInput, typeof(object)) as dynamic;

return myDynamic;
}
4/3/2012 8:40 PM | Thomas Watson
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

If like Tomas you get this error:

"no overload for method with 2 arguments"

On this line of code:
dynamic glossaryEntry = jss.Deserialize(json, typeof(object)) as dynamic;

...you are referencing the System.Web.Extensions from .Net 3.5 instead of .NET 4.0.

I had the same error and it drove me nuts because what compiled on my test app didn't work in my main app even though the code was practically identical.

And now we know... :-)

Shawn: Thanks very much for this code it worked a treat and saved me a lot of time.
4/16/2012 4:08 PM | BakaMike
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

it is amazing, You've saved my time. That's exactly what I've been looking for. Thank You :)
4/21/2012 11:19 AM | Tony
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

works perfectly!
5/1/2012 2:11 PM | Tamer
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

@BakaMike ... thanks for tip. This was driving me nuts too. And also a big thanks to Shawn. It was definitely a big help!
5/7/2012 9:21 PM | Ed
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

First off, thanks a lot for this. It's great!

But how do I access a property that's like:

jsonobject.thisthing.thatthing.thishas-ahyphen

It's the hyphen that's throwing me off. The only idea I have is replacing the - with _ in the entire string, but that will affect values as well. Any other ideas? Thanks in advance!
5/25/2012 10:28 AM | Kasey Krehbiel
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Kasey - that is an interesting problem. AFAIK you cannot have a dash in a C# property name. I think your idea of changing the property names to underscores is a valid one. Wonder if you alter the TryGetMember to do this work...

I have not tested this but what if you change:
result = this.Dictionary[binder.Name];
to
result = this.Dictionary[binder.Name] ?? this.Dictionary[binder.Name.Replace("_", "-")];

and
this.Dictionary.ContainsKey(binder.Name);
to
this.Dictionary.ContainsKey(binder.Name) || this.Dictionary.ContainsKey(binder.Name.Replace("_", "-"));
5/25/2012 11:01 AM | shawnweisfeld
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Hi,

I'm trying to use the code you wrote. I have multidimentional JSON. Example can be found at - http://api.crunchbase.com/v/1/company/1fastbite.js. How do I parse dynamic length dictionary TryGetMember?

Thanks for help!
6/5/2012 7:45 PM | Rahil Parikh
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Rahil, take a look at http://json.codeplex.com/ it now supports dynamic and might be a better choice then rolling your own...
6/6/2012 6:08 AM | shawnweisfeld
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Hi,

Thanks for the great work. I found a bug though with nested lists of json objects. To fix this, I've changed DynamicJsonObject.TryGetMember to:

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = Dictionary[binder.Name];

if (result is IDictionary<string, object>)
result = new DynamicJsonObject(result as IDictionary<string, object>);
else if (result is ArrayList)
{
var resultList = (result as ArrayList);
if (resultList.Count > 0 && resultList[0] is IDictionary<string, object>)
result = new List<DynamicJsonObject>((result as ArrayList).ToArray().Select(x => new DynamicJsonObject(x as IDictionary<string, object>)));
else
result = new List<object>((result as ArrayList).ToArray());
}

return Dictionary.ContainsKey(binder.Name);
}


A "grab bag" unit test that verifies this (and the other stuff you already had working) is:
[TestFixture]
public class DynamicJsonReaderTest
{
[Test]
public void GrabBag()
{
var json = ToJson(new List<string>
{
"{ ",
"\"glossary\": { ",
"\"title\": \"example glossary\", ",
"\"GlossDiv\": { ",
" \"title\": \"S\", ",
"\"GlossList\": { ",
"\"GlossEntry\": { ",
"\"ID\": \"SGML\", ",
"\"SortAs\": \"SGML\", ",
"\"GlossTerm\": \"Standard Generalized Markup Language\", ",
"\"Acronym\": \"SGML\", ",
"\"Abbrev\": \"ISO 8879:1986\", ",
"\"GlossDef\": { ",
"\"para\": \"A meta-markup language, used to create markup languages such as DocBook.\", ",
"\"GlossNames\": [ \"Mike\", \"Seta\" ], ",
"\"GlossSeeAlso\": [ { \"label\": \"GML\" }, { \"label\": \"XML\" }] ",
"}, ",
"\"GlossSee\": \"markup\" ",
"} ",
"} ",
"} ",
"} ",
"}",
});

dynamic dobj = DynamicJsonReader.Parse(json);

Assert.AreEqual("example glossary", dobj.glossary.title);
Assert.AreEqual("S", dobj.glossary.GlossDiv.title);

for (var i = 0; i < 2; i++)
Assert.AreEqual(i % 2 == 0 ? "Mike" : "Seta",
dobj.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossNames[i]);

for (var i = 0; i < 2; i++)
Assert.AreEqual(i%2 == 0 ? "GML" : "XML",
dobj.glossary.GlossDiv.GlossList.GlossEntry.GlossDef.GlossSeeAlso[i].label);

}

private static string ToJson(IEnumerable<string> lines)
{
var stringBuilder = new StringBuilder();
foreach (var line in lines)
stringBuilder.AppendLine(line);
return stringBuilder.ToString();
}
}


Take care,
Mike
7/23/2012 6:11 PM | Mike Bria
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Mike, take a look at http://json.codeplex.com/ it now supports dynamic and might be a better choice then rolling your own... (or having to deal with the bugs in my example). Alot has changed in the 2 years since I wrote this post. . .
7/24/2012 8:42 AM | shawnweisfeld
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

msdn.microsoft.com/.../hh770287.aspx

This is for metro style app.
7/26/2012 7:04 AM | ashish
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

i have this JSON structure .. HOW CAN I Parse it

{"count":1,"type":"polls","page":1,"size":6,"data":{"4013260820121230":{"10":{"102":{"id":"102","count":"2"},"103":{"id":"103","count":"1"},"104":{"id":"104","count":"2"}},"11":{"201":{"id":"201","count":"2"},"202":{"id":"202","count":"3"},"203":{"id":"203","count":"2"}}}}}
8/29/2012 8:20 AM | Yugam
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

Amazing, how long it took me to find this (as I didn't know about "dynamic").

I stumbled upon a similar problem as Kasey did. My problem was that one of the keys in the JSON data was "params" which is obviously a keyword in C#. The trick to solve both problems (without any rewriting) is to implement TryGetIndex along with TryGetMember. This gives you a JavaScript-like workaround, where you can access awkward field names through data["params"] or data["my-awkward-field-name"].
1/20/2013 9:28 AM | Martin Büttner
Gravatar

# re: Using C# 4.0 and dynamic to parse JSON

How can I loop through multipe records from Dynamic for Json data?
2/22/2013 3:02 PM | Gary Dhami

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 5 and 3 and type the answer here:

Powered by: