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.
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.
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.
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"
}
}
}
}
}
"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"
}
}
}
}
}
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.