I was asked on Friday by a friend how one can consume JSON in Silverlight. At the time, I just said start with the System.Json namespace and I’ll get you a sample later. Well, here’s a sample and a peek into my head because I couldn’t just stop with creating the sample that he needed.
Download Solution – PlayingWithJSON.zip
*Update – gotta do a quick shoutout to Leon Gersing for his passionate defense of JavaScript and JSON. It’s part of what inspired me to spend as much time on this as I did.
Really short JSON overview
JSON, for those of you who don’t know, is a really simple data format heavily used with JavaScript. It’s a lot smaller and lighter than XML.
This is the JSON that I’m using in this example. As you can see, it’s just name/value pairs with a little bit of extra formatting. Curly Braces separate objects in a list. Commas separate values. Colons separate the names from the values.
[
{“FirstName”:”Etta”,”LastName”:”James”,”Url”:”http:\/\/www.etta-james.com\/”},{“FirstName”:”Billie”,”LastName”:”Holiday”,”Url”:”http:\/\/www.billie-holiday.net\/”},{“FirstName”:”Ella”,”LastName”:”Fitzgerald”,”Url”:”http:\/\/en.wikipedia.org\/wiki\/Ella_Fitzgerald”}]
}
Creating the JSON service with ASP.NET MVC Framework
The first thing that I did was slap together an ASP.NET MVC Framework application to produce the JSON and host the Silverlight application. This was remarkably easy. I decided against going through the effort of creating a database because that’s not really the point of the exercise.
I created a simple class that to fill out and serialize and then returned it from a controller.
public class Artist { public string FirstName { get; set; } public string LastName { get; set; } public string Url { get; set; } }
The next step was to create a simple controller that would return my list as JSON. As you can see, there’s a JsonResult that you can return right from a controller method.
public class ArtistsController : Controller { public JsonResult GetAll() { List<Artist> artists = new List<Artist>(); artists.Add(new Artist("Etta", "James", "http://www.etta-james.com/")); artists.Add(new Artist("Billie", "Holiday", "http://www.billie-holiday.net/")); artists.Add(new Artist("Ella", "Fitzgerald", "http://en.wikipedia.org/wiki/Ella_Fitzgerald")); return Json(artists); } }
To test it, I just browsed to http://localhost:mylocalport/artists/getall. That returned the JSON file. As the browser doesn’t display JSON auto-magically, it prompted me to download a file and save it off.
Now, take notice of the URL. The ASP.NET MVC Framework is producing what’s called a RESTful Service. The parameters and all are simply in the URL. In the URL, “artists” takes us to a particular Controller called the ArtistsController and the “getall” calls the “GetAll()” method on that controller. We can further specify parameters and the like in the URL. Scott Guthrie has a great article that explains the URL routing and all at http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx.
Now that we are producing JSON from a services, we are ready to create a client.
Getting the JSON from the RESTful service.
The first client that I tried was the straight up System.Json namespace client. Since we are using a RESTful service, we can’t generate the proxy object in the same way that we’re used to with SOAP. Instead, we need to leverage the WebClient object. This is done as follows:
void mainButton_Click(object sender, RoutedEventArgs e) { Uri serviceUri = new Uri("/Artists/GetAll", UriKind.Relative); WebClient downloader = new WebClient(); downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(downloader_OpenReadCompleted); downloader.OpenReadAsync(serviceUri); }
Notice that the URL that I’m using is relative. You can specify the full URL if the service is not on your own server. These calls, as all calls in Silverlight, are async. Therefore I wire up the downloader_OpenReadCompleted event and call OpenReadAsync. Simple enough?
Leveraging System.Json
Once the downloader returns, I can get the result off of the event arguments. Load is a static method on the JsonArray that will automatically parse out the results from the stream object. Then, I can just loop over the objects in the array and use them to fill out my own type that I’m ready to databind to.
void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { JsonArray jsonArray = (JsonArray)JsonArray.Load(e.Result); List<Artist> artists = new List<Artist>(); foreach (JsonObject jsonArtist in jsonArray) { Artist artist = new Artist(); artist.FirstName = jsonArtist["FirstName"]; artist.LastName = jsonArtist["LastName"]; artist.Url = jsonArtist["Url"]; artists.Add(artist); } dataGrid1.ItemsSource = artists; }
This was fairly straight forward and simple, but I wasn’t ready to be done yet.
Leveraging LINQ
The next thing is to try LINQ against this JsonArray and see what happens.
void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { JsonArray jsonArray = (JsonArray)JsonArray.Load(e.Result); var query = from artist in jsonArray select new Artist { FirstName = (string)artist["FirstName"], LastName = (string)artist["LastName"], Url = (string)artist["Url"], }; List<Artist> artists = query.ToList() as List<Artist>; dataGrid1.ItemsSource = artists; }
Personally, I think that the LINQ version is a lot more powerful. It’s not immediately obvious in this simple example but there’s a ton that I could do with the LINQ query from filling out sub-types to filtering and so on.
JSON in JavaScript
But then I started thinking about all of the different ways that one can get and parse out JSON directly in JavaScript (I mean it does have JavaScript right in the name).
First, I did it the old school way that I used to do back when I was doing a ton of JavaScript development.
Quick note here – before you start copying this code – this is NOT the way to do this in the modern era. I did it to prove a point. You really need to be leveraging one of the many great JavaScript frameworks that are out there in order to accomplish your JavaScript today. I’ll get to those in a minute.
Short version, you’re going to be doing an XMLHttpRequest directly. When that returns you’ll get an “onreadystatechange” event which you can respond to. If it’s actually ready, then you can call a JavaScript function called eval that will return and array of objects parsed out from the string that you pass in.
<script language="javascript" type="text/javascript"> function GetJson(url) { // Create xmlhttprequest object var xmlhttp = null; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); //make sure that Browser supports overrideMimeType if (typeof xmlhttp.overrideMimeType != 'undefined') { xmlhttp.overrideMimeType('text/xml'); } } else if (window.ActiveXObject) { xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } else { alert('Perhaps your browser does not support xmlhttprequests?'); } // Create an HTTP GET request xmlhttp.open('GET', url, true); // Set the callback function xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { var json = xmlhttp.responseText; var artists = eval(json); var newArtistList; newArtistList = "<ul>"; for (var i in artists) { var artist = artists[i]; newArtistList += "<li>" + artist.FirstName + " " + artist.LastName + "</li>" } newArtistList += "<ul>"; outputControl = document.getElementById("divOutput"); outputControl.innerHTML = newArtistList; } else { // waiting for the call to complete } }; // Make the actual request xmlhttp.send(null); } </script>
Alright, that’s a lot of JavaScript to get through and understand. There are multiple possible problems here. First, that’s just a ton of code. Second, the same eval function that you are calling here is the same eval function used to dynamically execute a piece of JavaScript in a string. This means that you’re open to all kinds of possible attacks. Finally, there are a ton of frameworks that have come up in the past handful of years.
So, let’s go look at some of those frameworks.
Leveraging MS AJAX
The AJAX framework that I’ve been trying to learn lately is the MS AJAX one. It’s got a lot of power. First, notice that the WebRequest actually looks a lot like what I did in C#. Next, notice the JavaScriptSerializer object. This gives me some nice and easy serialization to and from JSON. The reason to use this is that it’s a much safer mechanism than eval. Lastly, notice the $get at the end. This is a nice and easy selector that does all of the heavy lifting of finding the object in the DOM and the like.
<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script> <script language="javascript" type="text/javascript"> function GetJson() { var request = new Sys.Net.WebRequest(); request.get_headers()['Content-Type'] = 'application/json; charset=utf-8'; request.set_url('/Artists/GetAll'); request.add_completed(Function.createDelegate(null, DisplayArtists)); request.invoke(); } function DisplayArtists(executor) { if (executor.get_responseAvailable()) { var json = executor.get_responseData(); var artists = Sys.Serialization.JavaScriptSerializer.deserialize(json); var newArtistList; newArtistList = "<ul>"; for (var i in artists) { var artist = artists[i]; newArtistList += "<li>" + artist.FirstName + " " + artist.LastName + "</li>" } newArtistList += "<ul>"; $get("divOutput").innerHTML = newArtistList; } } </script>
This is a lot less code and a lot safer than what I was doing in raw JavaScript. However, I thought I should try this with a few more frameworks.
Leveraging Prototype
A framework that I’ve used for a while and really like is Prototype.
This is a whole lot less code. Notice the “Ajax.Request”. That packages up all of the work of doing the network call behind one simple to use function. And on the response is a nice and easy method called evalJSON that will parse out the JSON string to an array of objects that you can loop over. Finally, notice the $() selector.
<script src="../../Scripts/prototype-1.6.0.2.js" type="text/javascript"></script> <script language="javascript" type="text/javascript"> function GetJson() { new Ajax.Request('/Artists/GetAll', { method: 'get', onSuccess: DisplayArtists }) } function DisplayArtists(transport) { var json = transport.responseText.evalJSON(); var newArtistList; newArtistList = "<ul>"; json.each(function(artist) { newArtistList += "<li>" + artist.FirstName + " " + artist.LastName + "</li>" }); newArtistList += "<ul>"; $("divOutput").insert(newArtistList); } </script>
I liked this a lot. A feature that I didn’t take advantage of is that Scriptaculous, which is a fantastic set of JavaScript libraries for visualization, weaves in with Prototype really easily.
Leveraging jQuery
The last JavaScript framework that I tried was jQuery. One extra cool part about this one for me is that I get intellisense right in Visual Studio. Check out Scott Guthrie’s post on jQuery Intellisense in VS2008 to see how to enable it.
Notice that it’s got the same type of easy to use function that packages up the call to get the JSON. Since it knows that it’s JSON, the variable returned to the callback is already parsed out into a usable object. And lastly, notice the $() selector again. There is a subtle difference between this one and the Prototype selector in that the jQuery one uses a # to signify that it’s looking for an ID, . to signify objects with a given class and so on.
<script language="javascript" type="text/javascript"> function GetJson() { $.getJSON("/Artists/GetAll", {}, DisplayArtists); } function DisplayArtists(data) { var newArtistList; newArtistList = "<ul>"; for(var i = 0; i < data.length;i++) { var artist = data[i]; newArtistList += "<li>" + artist.FirstName + " " + artist.LastName + "</li>" } newArtistList += "<ul>"; $("#divOutput").html(newArtistList); } </script>
Now that I was done playing with different JavaScript libraries, I thought I’d see if I could piece it all back together.
Leveraging jQuery and Silverlight
I decided to try jQuery and Silverlight together, leveraging jQuery to get and parse the JSON and Silverlight to do the display. This turned out to be remarkably simple.
First, in order for Silverlight to call into the JavaScript, you just have to call HtmlPage.Window.Invoke. That dynamically looks up and executes the method named in the first parameter.
void mainButton_Click(object sender, RoutedEventArgs e) { HtmlPage.Window.Invoke("GetJson", "/Artists/GetAll"); }
Now, I could have wired up the JavaScript to listen for the button click event directly but that’s a lot more code than what I’ve got here.
Next I had to enable the Silverlight objects for callbacks. Step 1 is to Register the class with the browser’s scripting engine.
public LeveragingJQuery() { InitializeComponent(); HtmlPage.RegisterScriptableObject("LeveragingJQuery", this); ...
}
Step 2 is to declare one of the methods as a ScriptableMember. This gives the scripting engine access to the method in question. There’s also ScriptableType for creating serializable objects but that’s not what we need here. Notice, however, that the object type passed in is a ScriptObject. The slick part about this object is that it’s got a “ConvertTo” method that will take the weakly typed objects that it contains and serialize them to the type of object that you specify. This made the C# code here REALLY simple.
[ScriptableMember] public void CallBackFromJavaScript(ScriptObject artists) { List<Artist> listofartists = artists.ConvertTo<List<Artist>>(); dataGrid1.ItemsSource = listofartists; }
What’s left is the JQuery in the browser that we’re talking to. Notice that the first part is exactly the same. The second part, however, is a little different. First, we have to get a reference to the Silverlight plugin itself. Since I’m in a form object, ASP.NET mangles the name a little bit but it wasn’t too hard to figure out the new name. Next, I have to get the object that I had registered with the RegisterScriptableObject method. Then, all that’s left is calling the method to pass in my array of Artists.
<script src="/Scripts/jquery-1.2.6.js" type="text/javascript"></script> <script language="javascript" type="text/javascript"> function GetJson(url) { $.getJSON(url, {}, DisplayArtists); } function DisplayArtists(artists) { var control = document.getElementById("ctl00_MainContent_SilverlightPlugin"); var leveragingJSPage = control.Content.LeveragingJQuery; leveragingJSPage.CallBackFromJavaScript(artists); } </script>
Conclusion
This is how I learn about new things. I’m not satisfied with just getting the job done in one and only one way. I want to try out 3, 4, 5 different ways to accomplish a given task. That way I know what the right way to do something is in a given context. For example, I don’t know that I’ll ever need to mingle jQuery and Silverlight. But, I know how it’s done now and know that it’s a lot simpler than I originally thought it would be.
JSON is a simple but powerful format. There are a ton of simple services, from flickr to twitter to many of your production applications that could be producing JSON. It saves bandwidth compared to XML and is a just as simple to use.