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.
51 comments:
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.
@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.
Thanks, that was a neat write up !
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
@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}"/>
Hi,nice tutorial.
Is it possible to store the unparsed response from the url in a string?
@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.
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.
@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?
{
"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?
@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?
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
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.
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
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.
@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 ;-)
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??
@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
Can you suggest me some links for this type of navigation tutorial???
@anup sure: http://www.silverlightshow.net/items/Windows-Phone-7-Part-3-Understanding-navigation.aspx
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).
@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.
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; }
}
@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?
you're right I'm sorry,
I want to create a listbox with research title and link
only this ..
Thank you for your attention
@Elia no need to be sorry. What URL did you get this code from? What did you paste into the code generator website?
I solved my problem, I created a class that was wrong, thank you for your time.
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??
@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.
sandeep-chourasia.blogspot.com
thanks i m actulaaly looking deserialize
{
"multicast_id": 5181701976783654000,
"success": 0,
"failure": 1,
"canonical_ids": 0,
"results": [
{
"error": "MismatchSenderId"
}
]
}
@sam could you please elaborate? What's your problem?
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\\\\\"}]}";
@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?
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...
@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.
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; }
@Akash, see solution a little higher http://dotnetbyexample.blogspot.com/2012/01/json-deserialization-with-jsonnet.html?showComment=1345028921161#c4689023687682152667
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
@ravi how about making deserialization classes for both occasions? If one fails, try the other.
Dear Friend, I am serializing and everything is working perfectly, I wonder how I sort my json for certain field (eg date field)
@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 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
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"));
}
@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?
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
@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
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...
@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?
Nice article. Nice and clear.
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'.
@Rayze have you tried downloading the sample and see what is different?
Post a Comment