22 January 2012

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:Json2Csharp

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"));
    }
  }
}

JsonSerializerAnd 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.

51 comments:

Steve said...

Is the Phone class the same as the RootObject class? The highlighted line of code uses the Phone class, but you declared it as RootObject earlier.

Joost van Schaik said...

@Sly: have you seen this text in the article?

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 and change it to Phone.

BragBoy said...

Thanks, that was a neat write up !

Muhammad Iril said...

Hi, I just learn how to parse Json in wp. could you give me an example how to show "Storage", "Memory", "Screensize" from Specs class?

thank you very much

Joost van Schaik said...

@Muhammad, I am not really sure what you mean. You mean show the other fields? If so, you just add a TextBlock to the StackPanel like this, for instance:
<TextBlock Text="{Binding Specs.ScreenSize}"/>

Narayana said...

Hi,nice tutorial.

Is it possible to store the unparsed response from the url in a string?

Joost van Schaik said...

@Narayana thank you I do my best ;-). What you get back from WebClient end up in r.EventArgs.Result - that's a string, and you can store that in any other string you like.

Ahmad said...

Nice tutorial, but wat if i av a nested json like this:
{"posts":[{"post":{"id":"2","Hosiptal":"Christ Medical Centre","Address":"28, Randle Road, Apapa, Lagos ","Tag":"Apapa","phone":"1-5870926"}},{"post":{"id":"8","Hosiptal":"Macey Medical Centre ","Address":"Capricorn Block Ground Floor, Eleganza Plaza, Apapa, Lago","Tag":"Apapa","phone":""}}]}
iv created 3 classes with json2c# online tool. How do access each variable.

Joost van Schaik said...

@femi well you get 3 objects: RootObject Post and Post2. RootObject contains a List, Post a single Post2 object. This as apparently because of the data structure. What is your problem exactly?

Unknown said...

{
"status": "ok",
"count": 3,
"categories": [
{
"id": 41,
"slug": "love-messages",
"title": "Love Messages",
"description": "If you have a girlfriend, boyfriend, wife, husband...a secret lover. Then these cute love messages and love quotes are for you. Romantic text messages galore find the perfect message to send to your lovers Mobile Phone.",
"parent": 0,
"post_count": 994
},
{
"id": 63,
"slug": "rajnikant-jokes",
"title": "Rajnikant Jokes",
"description": "Are you a Fan of Rajnikant? Here is the collection of some cool messages for Rajnikant. Share and have fun",
"parent": 0,
"post_count": 149
},
{
"id": 52,
"slug": "santa-banta-jokes",
"title": "Santa Banta Jokes",
"description": "Do you love Jokes? So, here is the best collection of Santa Banta jokes for you. Send awesome and funny SMS's to your friends!",
"parent": 0,
"post_count": 503
}
]
}


this is my json and i got the error in class file and near deserialization variable. can you solve this?

Joost van Schaik said...

@Anup:

You give very little to work on, so I work on a hunch. I guess you use
var deserialized = JsonConvert.DeserializeObject<List<YourObject>>(r.EventArgs.Result);

But your JSON suggest a SINGLE root object. So I'd suggest you try:
var deserialized = JsonConvert.DeserializeObject<YourObject>(r.EventArgs.Result);

Does that help?

Unknown said...

nop didn't work.....my class is


using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;


namespace PNF
{
public class Category
{
public int id { get; set; }
public string slug { get; set; }
public string title { get; set; }
public string description { get; set; }
public int parent { get; set; }
public int post_count { get; set; }
}

public class RootObject
{
public string status { get; set; }
public int count { get; set; }
public List categories { get; set; }
}
}

tell me exact code line and i have to display "title" and json url is http://www.mysmsbasket.com/wp/?json=get_category_index&dev=1

Joost van Schaik said...

Anup, if I add this classes generated by json2csharp to my demo project and change the code to this:

private void Load_Click(object sender, RoutedEventArgs e)
{
var w = new SharpGIS.GZipWebClient();
Observable.FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted")
.Subscribe(r =>
{
var deserialized = JsonConvert.DeserializeObject<RootObject>(r.EventArgs.Result);
MessageBox.Show(deserialized.categories[0].title);
//PhoneList.ItemsSource = deserialized;
});
w.DownloadStringAsync(new Uri("http://www.mysmsbasket.com/wp/?json=get_category_index&dev=1"));
}

I get the popup "Love Messages" which is the title of the first message. You should really be able to work out things from here now.

a.t.aydemir said...

Hi,

First of I would like to thank you for your example.

Everything worked correctly for me but I want to store the data gathered from the google URL.

Here is my detailed problem description:

http://stackoverflow.com/questions/11978239/jsonconvert-can-not-assign-string-value-to-a-string-variable

I will be very happy if you could help me.

Best Regards

Unknown said...

Thanks for your interest in my problem Joost. currently i am not in my own pc but i will try this again and will come to you. and thanks again.

Joost van Schaik said...

@a.t.aydemir, I looked at your problem at Stackoverflow. I posted a possible answer, assuming that I guessed right what you are doing wrong. A hint: next time provide a little more context, like how you are calling you method ;-)

Unknown said...

Hi Joost, its working for me now. great work. and need one more help. actually when i click on any list of "title" then in another page i want "content" of that "title". can you help me please??

Joost van Schaik said...

@Anup that goes way beyond the scope of this article ;-). I suggest you look up navigation and how to pass data between pages. It really isn't that hard

Unknown said...

Can you suggest me some links for this type of navigation tutorial???

Joost van Schaik said...

@anup sure: http://www.silverlightshow.net/items/Windows-Phone-7-Part-3-Understanding-navigation.aspx

J said...

I have a JSON result of a list of strings. Is there a way to get the list of strings into a ListBox using a ListBox.ItemTemplate still?

I can get the results into the listbox, by jut creating a list from the deserialized var, but there's very little customisation of the listbox without the item template (such as word wrapping etc).

Joost van Schaik said...

@J: Maybe you can try something with the tag in the binding? (e.g. StringFormat=\{0:0.00\} ). And it the end you can always use a converter of course.

Unknown said...

I want to serialize a custom search in google like this:
I do not understand how deo modify your code ... can you help me?

public class Url
{
public string type { get; set; }
public string template { get; set; }
}

public class NextPage
{
public string title { get; set; }
public string totalResults { get; set; }
public string searchTerms { get; set; }
public int count { get; set; }
public int startIndex { get; set; }
public string inputEncoding { get; set; }
public string outputEncoding { get; set; }
public string safe { get; set; }
public string cx { get; set; }
}

public class Request
{
public string title { get; set; }
public string totalResults { get; set; }
public string searchTerms { get; set; }
public int count { get; set; }
public int startIndex { get; set; }
public string inputEncoding { get; set; }
public string outputEncoding { get; set; }
public string safe { get; set; }
public string cx { get; set; }
}

public class Queries
{
public List nextPage { get; set; }
public List request { get; set; }
}

public class Context
{
public string title { get; set; }
public List> facets { get; set; }
}

public class SearchInformation
{
public double searchTime { get; set; }
public string formattedSearchTime { get; set; }
public string totalResults { get; set; }
public string formattedTotalResults { get; set; }
}

public class Metatag
{
public string progid { get; set; }
public string originator { get; set; }
}

public class CseImage
{
public string src { get; set; }
}

public class CseThumbnail
{
public string width { get; set; }
public string height { get; set; }
public string src { get; set; }
}

public class Pagemap
{
public List metatags { get; set; }
public List cse_image { get; set; }
public List cse_thumbnail { get; set; }
}

public class Item
{
public string kind { get; set; }
public string title { get; set; }
public string htmlTitle { get; set; }
public string link { get; set; }
public string displayLink { get; set; }
public string snippet { get; set; }
public string htmlSnippet { get; set; }
public string cacheId { get; set; }
public string formattedUrl { get; set; }
public string htmlFormattedUrl { get; set; }
public Pagemap pagemap { get; set; }
}

public class RootObject
{
public string kind { get; set; }
public Url url { get; set; }
public Queries queries { get; set; }
public Context context { get; set; }
public SearchInformation searchInformation { get; set; }
public List items { get; set; }
}

Joost van Schaik said...

@Elia you have to be a little more specific than that. It's nice you post a whole lot of code, but what am I supposed to do with int?

Unknown said...

you're right I'm sorry,
I want to create a listbox with research title and link
only this ..
Thank you for your attention

Joost van Schaik said...

@Elia no need to be sorry. What URL did you get this code from? What did you paste into the code generator website?

Unknown said...

I solved my problem, I created a class that was wrong, thank you for your time.

Kumar Gaurav said...

this code is causing "System.Net.WebException" when I click the load phones button...

var deserialized = JsonConvert.DeserializeObject>(r.EventArgs.Result);

What could be the problem??

Joost van Schaik said...

@Kumar

Impossible to tell from this snippet, especially since it is invalid code. I suggest you check if the URL exists at all and if you get a list of JSON objects - or a single root object. That is the most common cause for errors.

Anonymous said...

sandeep-chourasia.blogspot.com

thanks i m actulaaly looking deserialize

{
"multicast_id": 5181701976783654000,
"success": 0,
"failure": 1,
"canonical_ids": 0,
"results": [
{
"error": "MismatchSenderId"
}
]
}

Joost van Schaik said...

@sam could you please elaborate? What's your problem?

iceman said...

Hi,

I found your comments interesting.While i was using Json for compact framework i found that it is not capable of handling missing property declaration

for e.g below response deserialization fails saying "firstName1" is not declared. how to handle these
json = "{\"OperatorList\":[{\"operatorId\":\"22tt\",\"firstName1\":null,\"lastName\":\"\"},{\"operatorId\":\"22\",\"firstName\":\"a\",\"lastName\":\"t\\\\\"}]}";

Joost van Schaik said...

@iceman you will have to provide me some more code (find my e-mail address op on my blog). Did you follow my sample? Did you download my sample code?

Akash kakadiya said...

hello sir,
I want to display images which is getting with json response .i do alot search but i cant find anything .how can i do that.My c# class is here:
public class ProfileImage
{
public object Title { get; set; }
public object DisplayName { get; set; }
public string Value { get; set; }
}
public class OtherImage
{
public object Title { get; set; }
public object DisplayName { get; set; }
public string Value { get; set; }
public string ImageType { get; set; }
}
public class Images
{
public ProfileImage ProfileImage { get; set; }
public List OtherImages { get; set; }
}
public class UserImages
{
public Images Images { get; set; }
public string User { get; set; }
}
public class RootObject
{
public string ukey { get; set; }
public string skey { get; set; }
public UserImages UserImages { get; set; }
public Profile profile { get; set; }
public MatchParameters matchParameters { get; set; }
public Payment payment { get; set; }
public string MeetDistance { get; set; }
}
Please help..thank you in advance...

Joost van Schaik said...

@Akash kakadiya Please try to walk a mile in my shoes. I have no idea from what URL you have generated this code. I have no idea in what project you are using it. I am not clairvoyant you know ;-)

What I suggest you do: download a piece of the JSON and hunt for anything starting with http. If I must venture a guess, I think ProfileImage.Value and OtherImage.Value should contain image URL.

Good luck.

Akash kakadiya said...

Respected Sir,here i take json from www.json.org/example.html . Sir now if i want to display menuitem in listbox than how it can be possible...
public class Menuitem
{
public string value { get; set; }
public string onclick { get; set; }
}

public class Popup
{
public List menuitem { get; set; }
}

public class Menu
{
public string id { get; set; }
public string value { get; set; }
public Popup popup { get; set; }
}

public class RootObject
{
public Menu menu { get; set; }

Joost van Schaik said...

@Akash, see solution a little higher http://dotnetbyexample.blogspot.com/2012/01/json-deserialization-with-jsonnet.html?showComment=1345028921161#c4689023687682152667

Anonymous said...

Its a very nice tutorial..
I am stuck with a problem. In the json response that I receive an element can come either as json object or json array.. I am not able to handle this problem

ex:
the response can be this
{"posts":[{"id":"2"},{"id":"2"}]}
or
{"posts":{"id":"2"}}

Please help
I am using DataContractJsonSerializer

Joost van Schaik said...

@ravi how about making deserialization classes for both occasions? If one fails, try the other.

Idéias Mobile said...

Dear Friend, I am serializing and everything is working perfectly, I wonder how I sort my json for certain field (eg date field)

Joost van Schaik said...

@Idéias Mobile I don't quite understand what you mean. Deserialized data with a Date in it can simply be ordered with an .OrderBy() call, but I don't think that's what you mean

Joost van Schaik said...

@Idéias Mobile I don't quite understand what you mean. Deserialized data with a Date in it can simply be ordered with an .OrderBy() call, but I don't think that's what you mean

Idéias Mobile said...

Gostaria de ordenar os campos do meu json por data

Meu exemplo está funcionando mas trazendo os dados da maneira padrão (sem ordenação).

Não sei por onde começar!

private void Load_Click(object sender, RoutedEventArgs e)
{
var w = new WebClient();
Observable
.FromEvent(w, "DownloadStringCompleted")
.Subscribe(r =>
{
var deserialized =
var deserialized =
JsonConvert.DeserializeObject(r.EventArgs.Result);
PhoneList.ItemsSource = deserialized.time.toArray();
});
w.DownloadStringAsync(
new Uri("http://application-ideiasmobile.rhcloud.com/parazao/rest/servico/pegaTimes"));
}

Joost van Schaik said...

@Idéias Mobile my Portuguese is very bad (i.e. no existent but fortunately Bing translator helped. But there is no date in your json.

If you want to order by a field, say, cidade, you can try this;

PhoneList.ItemsSource = deserialized.time.OrderBy( p=> p.cidade).ToList();

You will need to add a using "System.Linq" on top of you C# file.

Does that help?

Unknown said...

Hello Sir,

This is my class

public class Answer
{
public string answerId { get; set; }
public string answer { get; set; }
}

public class Question
{
public string questionId { get; set; }
public string questionTitle { get; set; }
public string storyUrl { get; set; }
public string correctAnswerId { get; set; }
public List answers { get; set; }
}

public class RootObject
{
public string response { get; set; }
public string message { get; set; }
public string questionType { get; set; }
public string device_id { get; set; }
public string quiz_type { get; set; }
public int totalQuestion { get; set; }
public List questions { get; set; }
}

i want my question will be bind on a textblock with their respective option in a radio button , a submit button to check the option select on radio button & a next button to get next question

I do following steps to achieve that:-

public partial class Quiz : PhoneApplicationPage
{
WebClient wc = new WebClient();

public static string val;

private static IEnumerator iterator;
public Quiz()
{
InitializeComponent();
GetDeviceUniqueID();

wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
wc.DownloadStringAsync(new Uri("MY url"));
}

public static byte[] GetDeviceUniqueID()
{
byte[] result = null;
object uniqueId;
if (DeviceExtendedProperties.TryGetValue("DeviceUniqueId", out uniqueId))
{
result = (byte[])uniqueId;
}
val = Convert.ToBase64String(result);
return result;
}
protected void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var rootObject = JsonConvert.DeserializeObject(e.Result);
List obj = new List();

rootObject.device_id = val;

Question ques = new Question
{
questionTitle = rootObject.questions.Last().questionTitle,
answers = rootObject.questions.Last().answers.Select(ans => new Answer { answer = ans.answer, answerId = ans.answerId }).ToList(),
questionId = rootObject.questions.Last().questionId,
storyUrl = rootObject.questions.Last().storyUrl,
correctAnswerId = rootObject.questions.Last().correctAnswerId

};
txtQuestion.Text = ques.questionTitle;
rb1.Content = ques.answers.ElementAt(0).answer;
rb2.Content = ques.answers.ElementAt(1).answer;
rb3.Content = ques.answers.ElementAt(2).answer;
rb4.Content = ques.answers.ElementAt(3).answer;


}
I know this will only bind last question.
my problem is how to get next question on "Next" Button.
and check answer on "Submit" button.

please help me on this ..
I am a beginner in windows phone

Joost van Schaik said...

@rishiraj how about in stead of using LINQ just get your question data with and index? In stead of rootObject.questions.Last() rootObject.questions.[i]? I would have to see your whole app to understand what you are trying to do, but if you use and index and just do i=i+1 on next, you are halfway there I think

Unknown said...

Hello sir, thanks for reply..
as you see in code that rootobject hold all the properties of RootObject class.Now in this object i bind the data from URL in my textblock & radio button.

but how do i bind answerid & correctanswerid on a button say "SUBMIT" to check which option user select.

similarly how do i get next question on "NEXT" button click.

please help me on this...

Joost van Schaik said...

@rishiraj My dear friend, you paste a bunch of code on my blog, which is a part of your project, I presume. I have to guess from a few lines description what you are trying to do. I will first have to reconstruct your project, fill in the missing details... can't you put your project on a SkyDrive/OneDrive so I can download and have a look at it?

aungthu07 said...

Nice article. Nice and clear.

Razye said...

I'm working on windows Phone 7 and got this error: The type 'System.Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

Joost van Schaik said...

@Rayze have you tried downloading the sample and see what is different?