Click here to Skip to main content
15,884,472 members
Articles / Desktop Programming / WPF

Parse JSON to C# in WinRT, Silverlight, WPF, Windows Phone

Rate me:
Please Sign up or sign in to vote.
4.92/5 (5 votes)
5 Apr 2013CPOL2 min read 41.2K   471   15   8
How to parse JSON to C# in WinRT, Silverlight, WPF, Windows Phone.

Introduction

I have never worked with JSON before. But due to security limitations it’s the only simple way to get Twitter data in Silverlight. So I decided to do the same sample application for WinRT, Silverlight, WPF and Windows Phone platforms. As usual, there are many different ways to do the same thing. Jokingly, almost all JSON stuff, included into the box, is different on different platforms. Perhaps MS guys don't like to re-use code. Finally I’ve found the single common thing, which can be used everywhere. It’s System.Runtime.Serialization.Json.DataContractJsonSerializer. In WinPhone and Silverlight 5 applications it is included into the System.Servicemodel.Web assembly, in WPF – into System.Runtime.Serialization, and in WinRT it’s the part of .NET for Windows Store apps.

This sample uses twitter search api 1.0 which doesn’t require any authentication (https://dev.twitter.com/docs/api/1/get/search) .

To work with DataContractJsonSerializer you should generate the set of classes to which your JSON can be deserialized. It might be tricky if you want to do it by yourself. Fortunately, you can use the public service like json2csharp or offline tools like JSON C# Class Generator. They take JSON string and generate the set of c# classes. Then you can copy these classes to your project and use them.

The easiest way to get JSON string is to download it using the same uri which you are planning to use in your application. In Silverlight you can use WebClient.DownloadStringAsync:

C#
var client = new WebClient();
client.DownloadStringCompleted += (sender, args) =>
     {
          string json = args.Result;
     };
client.DownloadStringAsync(new Uri(yourUri));

My search uri looks like this one: http://search.twitter.com/search.json?q=from:ComponentOne&rpp=20&include_entities=true

Twitter returns quite long JSON string for my query, I won’t post it here. Just copy the string and ask json2csharp to convert it. For my case it gave me the next set of classes:

C#
public class Hashtag
{
    public string text { get; set; }
    public List<int> indices { get; set; }
}

public class Url
{
    public string url { get; set; }
    public string expanded_url { get; set; }
    public string display_url { get; set; }
    public List<int> indices { get; set; }
}

public class Entities
{
    public List<Hashtag> hashtags { get; set; }
    public List<Url> urls { get; set; }
    public List<object> user_mentions { get; set; }
}

public class Metadata
{
    public string result_type { get; set; }
}

public class Result
{
    public string created_at { get; set; }
    public Entities entities { get; set; }
    public string from_user { get; set; }
    public int from_user_id { get; set; }
    public string from_user_id_str { get; set; }
    public string from_user_name { get; set; }
    public object geo { get; set; }
    public object id { get; set; }
    public string id_str { get; set; }
    public string iso_language_code { get; set; }
    public Metadata metadata { get; set; }
    public string profile_image_url { get; set; }
    public string profile_image_url_https { get; set; }
    public string source { get; set; }
    public string text { get; set; }
    public object to_user { get; set; }
    public int to_user_id { get; set; }
    public string to_user_id_str { get; set; }
    public object to_user_name { get; set; }
}

public class RootObject
{
    public double completed_in { get; set; }
    public long max_id { get; set; }
    public string max_id_str { get; set; }
    public int page { get; set; }
    public string query { get; set; }
    public string refresh_url { get; set; }
    public List<Result> results { get; set; }
    public int results_per_page { get; set; }
    public int since_id { get; set; }
    public string since_id_str { get; set; }
}

Believe or not, it was the most complicated part. Note, if some part of returned JSON is empty, for example, there is no any data for urls in the entities, code generation tools will not create Url class and will use object type instead. In such case you won’t be able to parse urls. So, check such things and try to get different JSON which contains needed data.

You can use above classes directly, but I decided to use them for parsing only. Returned JSON might change upon service developers decision, I don’t want to change my XAML every time. So, in my samples I use really simple class to represent twitter item:

C#
public class TwitterItem
{
    public DateTime PublishDate { get; set; }
    public string Handle { get; set; }
    public string Title { get; set; }
    public string Link { get; set; }
}

And the last peace of work to get real data from server. Here is Silverlight version (the same exact code can be shared for WPF and Windows Phone platforms):

C#
/// <summary>
/// Searches and gets the index of the first older item already in the list
/// </summary>
/// <param name="date">The date of the item being compared to the list</param>
/// <returns>The index of the first oldest item, otherwise the number of items in the list</returns>
public static int GetFirstIndexOfOlderTwitterItem(DateTime date, ObservableCollection<TwitterItem> tItems)
{
    if (tItems.Count > 0)
    {
        TwitterItem tfi = tItems.FirstOrDefault(d => d.PublishDate < date);
        if (tfi == null)
            return tItems.Count;
        else
            return tItems.IndexOf(tfi);
    }
    else
    {
        return 0;
    }
}

/// <summary>
/// Loads twitter items into the specified control.
/// </summary>
/// <param name="uri">The uri to load twitter data.</param>
/// <param name="control">If set, method uses it to load photos only with this specific tag.</param>
public static void Load(string uri, Control control)
{
    ObservableCollection<TwitterItem> result = new ObservableCollection<TwitterItem>();
    var client = new WebClient();

    client.OpenReadCompleted += (s, e) =>
        {
            #region ** parse twitter data
            using (System.IO.Stream stream = e.Result)
            {
                // parse Json data
                DataContractJsonSerializer rootSer = new DataContractJsonSerializer(typeof(RootObject));

                RootObject root = (RootObject)rootSer.ReadObject(stream);
                foreach (Result res in root.results)
                {
                    TwitterItem ti = new TwitterItem();
                    ti.Title = res.text;
                    DateTimeOffset dateOffset;
                    if (DateTimeOffset.TryParse(res.created_at, out dateOffset))
                    {
                        ti.PublishDate = dateOffset.UtcDateTime;
                    }
                    ti.Handle = res.from_user;
                    if (res.entities.urls != null && res.entities.urls.Count > 0)
                    {
                        ti.Link = res.entities.urls[0].url;
                    }
                    result.Insert(GetFirstIndexOfOlderTwitterItem(ti.PublishDate, result), ti);
                }
                if (control is ItemsControl)
                {
                    ((ItemsControl)control).ItemsSource = result;
                }
            }
            #endregion
        };
    client.OpenReadAsync(new Uri(uri));
}

And a bit different code for WinRT platform:

C#
/// <summary>
/// Loads twits.
/// </summary>
/// <param name="uri">The uri to load twitter data.</param>
/// <returns></returns>
public static async Task<ObservableCollection<TwitterItem>> Load(string uri)
{
    ObservableCollection<TwitterItem> result = new ObservableCollection<TwitterItem>();
    try
    {
        var client = WebRequest.CreateHttp(uri);
        var response = await client.GetResponseAsync();

        #region ** parse twitter data
        using (System.IO.Stream stream = response.GetResponseStream())
        {
            // parse Json data
            DataContractJsonSerializer rootSer = new DataContractJsonSerializer(typeof(RootObject));

            RootObject root = (RootObject)rootSer.ReadObject(stream);
            foreach (Result res in root.results)
            {
                TwitterItem ti = new TwitterItem();
                ti.Title = res.text;
                DateTimeOffset dateOffset;
                if (DateTimeOffset.TryParse(res.created_at, out dateOffset))
                {
                    ti.PublishDate = dateOffset.UtcDateTime;
                }
                ti.Handle = res.from_user;
                if (res.entities.urls != null && res.entities.urls.Count > 0)
                {
                    ti.Link = res.entities.urls[0].url;
                }
                result.Insert(GetFirstIndexOfOlderTwitterItem(ti.PublishDate, result), ti);
            }
        }
        #endregion
    }
    catch
    {
    }
    return result;
}

Thanks to people who keep posting on http://stackoverflow.com/, most of the problems got solved after reading answers.

History

  • 15 Feb 2013 - First published.
  • 5 Apr 2013 - Added working Silverlight sample showing results in the ListBox.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Program Manager GrapeCity
Russian Federation Russian Federation
I'm the GrapeCity program manager for ComponentOne Studio.
If you need more info, learn about our products here: http://www.grapecity.com/

Comments and Discussions

 
QuestionIntegrating in windows phone Pin
syed fraz ali4-Apr-13 11:14
syed fraz ali4-Apr-13 11:14 
AnswerRe: Integrating in windows phone Pin
Irina Pykhova5-Apr-13 4:54
professionalIrina Pykhova5-Apr-13 4:54 
I attached the working sample for Silverlight, you can download it. The pattern is the same for all platforms: to show some collection in the ItemsControl, you should set ItemsControl.ItemsSource property and specify ItemsControl.ItemTemplate so that control knows, what properties of your business object and in which form it should show. For example:
C#
public partial class MainPage : UserControl
{
    public MainPage()
    {
        InitializeComponent();
        Loaded += Page_Loaded;
    }

    void Page_Loaded(object sender, RoutedEventArgs e)
    {
        // FeedData.Load will set ListBox.ItemsSource property to the returned collection
        FeedData.Load(string.Format(FeedData.Link, "ComponentOne"), this.twitterBox);
    }
}

and xaml:
HTML
<UserControl x:Class="SLTwitterSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox x:Name="twitterBox">
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate> <!-- use TwitterItem class as DataContext for individual item -->
                <DataTemplate>
                    <Grid HorizontalAlignment="Stretch" Background="SteelBlue" Margin="2">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Handle}" Margin="6" Foreground="White" FontWeight="Bold"/>
                        <TextBlock Grid.Row="1" Text="{Binding Title}" Foreground="WhiteSmoke" TextWrapping="Wrap" Margin="20 12"/>
                        <HyperlinkButton Content="{Binding Link}" NavigateUri="{Binding Link}" TargetName="_blank" Foreground="White" FontSize="10" Margin="15 20" Padding="0"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

QuestionAPI 1.1 Pin
Member 96319492-Mar-13 8:54
Member 96319492-Mar-13 8:54 
AnswerRe: API 1.1 Pin
Irina Pykhova2-Mar-13 9:06
professionalIrina Pykhova2-Mar-13 9:06 
GeneralRe: API 1.1 Pin
Member 96319492-Mar-13 10:44
Member 96319492-Mar-13 10:44 
AnswerRe: API 1.1 Pin
Irina Pykhova12-Jul-13 0:48
professionalIrina Pykhova12-Jul-13 0:48 
QuestionThe source code for the Class Generator Pin
Dewey15-Feb-13 15:52
Dewey15-Feb-13 15:52 
GeneralRe: The source code for the Class Generator Pin
Irina Pykhova16-Feb-13 0:09
professionalIrina Pykhova16-Feb-13 0:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.