JSON deserialization with JSON.net - basics
I’ve been contemplating an article about handling JSON for some time now, but it turned out to be a rather long article. So I’d thought to try something new, and write a short series in three parts.
- Part 1 handles the basics
- Part 2 handles advanced deserialization with class hierarchies
- Part 3 handles a caching-and-updating scenarios.
And this is part 1 ;-)
This whole article actually boils down to one line of code, but I need to go trough some hooplah to show you how to use it. It all begins with the data. Consider this piece of quite readable JSON, describing a few recent Windows Phone models.
[ { "Brand": "Nokia","Type" : "Lumia 800", "Specs":{"Storage" : "16GB", "Memory": "512MB","Screensize" : "3.7"} }, { "Brand": "Nokia", "Type" : "Lumia 710", "Specs":{"Storage" : "8GB","Memory": "512MB","Screensize" : "3.7"} }, { "Brand": "Nokia","Type" : "Lumia 900", "Specs":{"Storage" : "8GB", "Memory": "512MB","Screensize" : "4.3" } }, { "Brand": "HTC ","Type" : "Titan II", "Specs":{"Storage" : "16GB", "Memory": "512MB","Screensize" : "4.7" } }, { "Brand": "HTC ","Type" : "Radar", "Specs":{"Storage" : "8GB", "Memory": "512MB","Screensize" : "3.8" } } ]
JSON is rather compact, which is a great feature when you are developing for mobile devices. It has also a few downsides as far as client programming is concerned:
- generating client code for it that does all the parsing and calling, as for SOAP, is not a standard feature of Visual Studio,
- it’s almost impossible to read for an ordinary human being,
- deciphering it into classes is a lot of work,
- hand coding a parser for it is not fun.
Which is why you don’t. There are several ways of generating classes from JSON, the simplest way is this website: json2csharp by Jonathan Keith. You copy a JSON result into the upper textbox, hit the “Generate” button and out come your classes:
There are more sites that do the same, by the way, but this is what I use. Next steps:
- Fire up Visual Studio
- Create a new Windows Phone project (for instance JsonDemo)
- Plonk the classes generated above in the project. Bonus cookies if you split them in separate files and add namespaces to them. Bonus donut if you, like me, think “RootObject” is actually a pretty ugly name for an object - so change it to "Phone".
- Click Tools/Library Package Manager/Manage NuGet Packages for Solution (you do have the NuGet Package Manager installed, don’t you? If not, stop whatever you are doing now and get it right this instance, you hear me ;)? )
- Search for JSON.Net
- Click install. This will add a reference to NewtonSoft.Json.dll to your product.
- Add references to Microsoft.Phone.Reactive and System.Observable because they are going to be needed in the next step.
To make the result visible, add some XAML to the default content panel in Mainpage.Xaml – just a button and a templated ListBox, no rocket science here:
<StackPanel> <Button Name="Load" VerticalAlignment="Top" Content="Load phones" Click="Load_Click" /> <ListBox x:Name="PhoneList" Height="532"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Brand}" Margin="0,0,12,0" /> <TextBlock Text="{Binding Type}"/> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel>
Finally, open MainPage.Xaml.cs and add the method Load_Click as displayed below.
using System; using System.Collections.Generic; using System.Net; using System.Windows; using Microsoft.Phone.Controls; using Microsoft.Phone.Reactive; using Newtonsoft.Json; namespace JsonDemo { public partial class MainPage : PhoneApplicationPage { // Constructor public MainPage() { InitializeComponent(); } private void Load_Click(object sender, RoutedEventArgs e) { var w = new WebClient(); Observable .FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted") .Subscribe(r => { var deserialized = JsonConvert.DeserializeObject<List<Phone>>(r.EventArgs.Result); PhoneList.ItemsSource = deserialized; }); w.DownloadStringAsync( new Uri("http://www.schaikweb.net/dotnetbyexample/JSONPhones1.txt")); } } }
And there it is, the one line of code that this is all about. Call the DeserializeObject method, template it with the return type you want, and stuff the JSON string in it. Result: a list of objects with their properties filled, even if there are things like nested objects (specs in these case) and arrays in there.
If you run the demo solution you get the result displayed in the image on the right. Keep in mind this code is by no means Windows Phone specific. There are JSON.Net implementations for virtually all frameworks available. So should you feel the need to use this from Silverlight or full .NET: it’s there.
You should, by the way, pay attention to the structure of the JSON. The code I show works for a list. A list in JSON starts with a square bracket: [. If your JSON starts with a curly brace: { then you get returned a single object - a so called root object. In that case, your deserialization should code return a single object in stead of a list as well, i.e. something like
var deserialized = JsonConvert.DeserializeObject<Phone>(r.EventArgs.Result);
Finally, a ninja tip:
- Click Tools/Library Package Manager/Manage NuGet Packages for Solution again
- Search for SharpGIS.GZipWebClient
- Click install
- Change “WebClient” in the Load_Click method to SharpGIS.GZipWebClient
This plug-in replacement for WebClient by Morten Nielsen adds support for GZIP compressed web requests – this reduces network traffic even further, apparently boosting load performance significantly. You won’t really notice the difference on such a small data files as used in this sample, but as your JSON return values get larger, so will be the impact of using this library.
For the record: I am not ill nor do I have forsaken MVVM, but I tried to make the example as simple as possible so yes, I used a little code behind, as to not to cloud the solution in architectural frills. ;-)
Thanks to Matthijs Hoekstra for putting me on track to this, and to fellow #wp7nl developer Leon Zandman for correcting some annoying typos.