Condividi tramite


Connect Windows Phone 7 Apps to WordPress using JSON

json

This post was originally published on my main blog: https://kevinashley.com

Download Code Samples

This is the Part 2 of connectivity posts for Windows Phone. In Part 1 we discussed how to connect a Windows Phone app to Wordpress using the blog’s built-in XMLRPC mechanism. This time we’ll use another popular protocol called JSON.

Many of us use Wordpress: it's an excellent platform for bloggers. Every time I start a new Web project, before deciding to build it from scratch, I try to consider Wordpress or other CMS to save time and effort. Wordpress plugins fills many gaps, including eCommerce, Membership, CMS and hundreds of other use cases. If you are used to building everything from scratch, you probably smell danger in this lego mentality. Well, it makes sense, considering that 60% of software projects fail on time and on the budget. The paradigm of “standing on the shoulders of the giants” suddenly makes sense in the risky business of building software!

As a Windows Phone app developer you probably wonder how can you connect your Windows Phone 7 app to your Wordpress blog? Wordpress' standard built-in mechanism is XMLRPC, but with a help of a free plugin called JSON API, Wordpress can be accessed with JSON.

The only file we change in this example from the standard template Visual Studio has for a Panorama project is MainViewModel.cs. Cool, isn’t it?

Goals

In this project we’ll explore Windows Phone 7 connectivity with Wordpress using popular JSON API. We’ll build an app that retrieves a list of posts from Wordpress and shows them on our Windows Phone app.

Creating a Windows Phone 7 Project

We’ll create a Windows Phone 7 Panorama project. Panorama control is really cool Smile It started as an open source project on CodePlex and are now shipped as part of the Windows Phone Developer Tools. In Visual Studio, create a new Windows Phone Panorama Application.

image_thumb

Panorama control takes advantage of MVVM (Model-View-View-Model) pattern. If you haven’t heard of it, this wiki is a good starting point, but this pattern is great solid foundation for separating business logic from the presentation. Trust me, when you hit a few thousand lines, you will need a pattern like this, or you may get lost in the woods (code).

After building our template project, we’ll see something like this in the emulator. As you can see, our template loads sample items into our Panorama control. All this magic happens in the MainViewModel.cs file in the LoadData() method.

Install and activate JSON API Plugin on Wordpress

This step is needed on the Wordpress side before we can proceed. Wordpress doesn’t have a built-in JSON API, but there’s a plugin that provides it. In Wordpress, simply login as Administrator, go to Plugins, Add New and search for JSON API plugin. Install it an activate: you’re done! Important consideration: JSON API doesn’t provide any security, I suggest you code a filter to pass all JSON requests through your authentication hook.

Using JSON with Windows Phone 7

We need to reference two additional assemblies for the Visual Studio project we just created: System.Runtime.Serialization and System.ServiceModel.Web. They contain classes we need to access JSON API via REST requests and parse JSON results into C# classes.

image

Once you’ve added these assemblies as references, go ahead and open MainViewModel.cs. Add these namespaces at the top:

 using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Net;
using System.Net.Browser;

Now, let’s add a couple of helper classes that will hold our Wordpress posts responses from Json get_recent_posts call. JSON API documentation gives instructions on what fields are going to be in the response for each call, so I simply constructed these classes keeping in mind what I actually need for my Windows Phone 7 app.

 [DataContract]
public class Post
{
    [DataMember]
    public int id;
    [DataMember]
    public string type;
    [DataMember]
    public string slug;
    [DataMember]
    public string title;
    [DataMember]
    public string content;
    [DataMember]
    public string excerpt;
}

[DataContract]
public class Posts
{
    [DataMember]
    public int count;
    [DataMember]
    public int count_total;
    [DataMember]
    public List<Post> posts;
}

 

Notice DataContract and DataMember attributes. These are used by .NET built-in class called DataContractJsonSerializer. This class that .NET kindly provides for us will parse stream data from the Web server and map this data to the above fields and classes.

Using MVVM classes to load posts from Wordpress

Our goal is to pull recent Wordpress posts into our Windows Phone 7 app and display them as items of our Panorama control. Panorama control template project automatically generates all Model-View-View-Model (MVVM) classes we need. We’ll replace the project’s LoadData method with our own creation. Remember to replace MYBLOGURL with the URL of your blog with the JSON API plugin installed!

 public void LoadData()
{
    WebRequest.RegisterPrefix("https://MYBLOGURL", WebRequestCreator.ClientHttp);
    Uri serviceUri = new Uri("https://MYBLOGURL/?json=get_recent_posts");
    WebClient downloader = new WebClient();
    downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(downloader_OpenReadCompleted);
    downloader.OpenReadAsync(serviceUri);
}

Next we’ll add a method to receive the event when the response is returned from the Web server. We want to be a good Windows Phone app, so we’re using asynchronous non-blocking calls!

 void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if (e.Error == null)
    {
        try
        {
            Stream responseStream = e.Result;
            DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Posts));
            Posts response = (Posts)ser.ReadObject(responseStream);
            if (response.posts != null && response.posts.Count > 0)
            {
                foreach (Post post in response.posts)
                {
                    this.Items.Add(new ItemViewModel() { LineOne = post.title, LineTwo = post.excerpt });
                }
            }
        }
        catch (Exception x)
        {
            return;
        }
        this.IsDataLoaded = true;
    }
}

Compile your app and run it, et voila!

image_thumb4

How Does It Work?

In our LoadData method we asynchronously call our blog’s JSON API plugin, using REST. When the result arrives we deserialize it into .NET DataContract classes and then create Windows Phone Panorama control items with the titles of our Wordpress posts. You can extend this example for your app, but this gives you a great starting point and all low level plumbing.

Download Code Samples

This is how the completed MainViewModel.cs looks like after all changes we made in this post.

 using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Net;
using System.Net.Browser;

namespace WpWordpressJson
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public MainViewModel()
        {
            this.Items = new ObservableCollection<ItemViewModel>();
        }

        /// <summary>
        /// A collection for ItemViewModel objects.
        /// </summary>
        public ObservableCollection<ItemViewModel> Items { get; private set; }

        private string _sampleProperty = "Sample Runtime Property Value";
        /// <summary>
        /// Sample ViewModel property; this property is used in the view to display its value using a Binding
        /// </summary>
        /// <returns></returns>
        public string SampleProperty
        {
            get
            {
                return _sampleProperty;
            }
            set
            {
                if (value != _sampleProperty)
                {
                    _sampleProperty = value;
                    NotifyPropertyChanged("SampleProperty");
                }
            }
        }

        public bool IsDataLoaded
        {
            get;
            private set;
        }

        /// <summary>
        /// Creates and adds a few ItemViewModel objects into the Items collection.
        /// </summary>
        public void LoadData()
        {
            WebRequest.RegisterPrefix("https://MYBLOGURL", WebRequestCreator.ClientHttp);
            Uri serviceUri = new Uri("https://MYBLOGURL/?json=get_recent_posts");
            WebClient downloader = new WebClient();
            downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(downloader_OpenReadCompleted);
            downloader.OpenReadAsync(serviceUri);
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (null != handler)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                try
                {
                    Stream responseStream = e.Result;
                    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Posts));
                    Posts response = (Posts)ser.ReadObject(responseStream);
                    if (response.posts != null && response.posts.Count > 0)
                    {
                        foreach (Post post in response.posts)
                        {
                            this.Items.Add(new ItemViewModel() { LineOne = post.title, LineTwo = post.excerpt });
                        }
                    }
                }
                catch (Exception x)
                {
                    return;
                }
                this.IsDataLoaded = true;
            }
        }
    }

    [DataContract]
    public class Post
    {
        [DataMember]
        public int id;
        [DataMember]
        public string type;
        [DataMember]
        public string slug;
        [DataMember]
        public string title;
        [DataMember]
        public string content;
        [DataMember]
        public string excerpt;
    }

    [DataContract]
    public class Posts
    {
        [DataMember]
        public int count;
        [DataMember]
        public int count_total;
        [DataMember]
        public List<Post> posts;
    }
}

Download Code Samples