Parsing Arbitrary JSON in .NET Code
One of the emerging popular formats for sharing content online is JSON, shorthand for JavaScript Object Notation. Intended to be slightly more efficient than XML, it's found a home in lots of AJAX powered web applications. Some companies actually require you talk to their APIs in terms of JSON too, so having the ability to parse and emit arbitrary JSON in a strongly typed way is a valuable tool to have. As of .NET 3.5 SP1, we are equipped with the DataContractJsonSerializer
.
This tutorial will show you how to take an arbitrary piece of JSON and convert it into an object. Let's take a feed from the popular Flikr photo sharing site. Taking the latest public photos feed, you might get JSON such as:
jsonFlickrFeed({
"title": "Uploads from everyone",
"link": "http://www.flickr.com/photos/",
"description": "",
"modified": "2009-02-04T07:24:51Z",
"generator": "http://www.flickr.com/",
"items": [
{
"title": "Ohayo2009-224",
"link": "http://www.flickr.com/photos/14427733@N07/3252849052/",
"media": {
"m":"http://farm4.static.flickr.com/3534/3252849052_ab77f0e3af_m.jpg"
},
"date_taken": "2009-01-29T16:47:42-08:00",
"description": "Description of photo appears here",
"published": "2009-02-04T07:24:51Z",
"author": "Author of photos appears here",
"author_id": "14420000@N00",
"tags": "2009 ohayo"
},
.. more items
]
})
Break the Data into Objects
The trick now is to try and map the data structure into a set of .NET objects, and map each property on our objects to a field from the feed using the DataMember
attribute. The classes themselves will be annotated with the DataContract
attribute. Let's look at how we might do this for an 'item' from the Flikr feed we're using:
[DataContract()]
public class FlikrItem : IExtensibleDataObject
{
[DataMember(Name="title")]
public String Title { get; set; }
[DataMember(Name = "link", IsRequired = true)]
public String Link { get; set; }
[DataMember(Name="media")]
public FlikrMedia Media { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
As you can see, we've connected the elements from the JSON object to our .NET properties by using DataMember
attributes. We've also implemented the IExtensibleDataObject
interface, and told WCF to ignore any elements we haven't mapped explicitly and place them into what is effectively a bucket. Note that the media
property is mapped to an instance of FlikrMedia
, another type we have to declare to store the actual media imagery. All in all, our object model will look somewhat like:
Reading the JSON
Next, we have to read the JSON data into our objects using the DataContractJsonSerializer
. Before we do this though, we have to manually process the string to remove the callback wrapping around the real JSON data, since the jsonFlikrFeed()
wrapping will otherwise cause parsing to fail. Next, we have to convert the string we've downloaded from the API into a buffer of bytes, place it into a memory stream, and then pass that through to the serializer.
static FlikrFeed ParseFeed(String inputContent)
{
inputContent = inputContent.Trim();
if (inputContent.StartsWith("jsonFlickrFeed("))
inputContent = inputContent.Remove(0, "jsonFlickrFeed(".Length);
if (inputContent.EndsWith(")"))
inputContent = inputContent.Substring(0, inputContent.Length-1);
DataContractJsonSerializer serializer =
new DataContractJsonSerializer(typeof(FlikrFeed));
using (MemoryStream stream =
new MemoryStream(Encoding.Unicode.GetBytes(inputContent)))
{
return serializer.ReadObject(stream) as FlikrFeed;
}
}
And, that is that! We've now got an instance of FlikrFeed
we can browse the Items
collection of, and also drill down to the media item preview image. You can apply these principles to any JSON feed you wish to interact with!
Notes
- For more general information about JSON, there's a fairly good introduction to the basics of JSON at Google's GWT FAQ. This introduces the idea of wrapping JSON inside callback functions (as
flikr
has done in our example). - You need the .NET Framework 3.5 SP1 installed in order to run this code, and you can find the serializer in the
System.ServiceModel.Web
namespace.