Many types of services on the
Internet are easy to consume with JavaScript on web pages and use a
special type of data called JavaScript Object Notation (JSON). This
format is useful because it is easy to consume from JavaScript and
tends to be smaller than XML for certain types of data. As you work
with different Internet services, you will find a number of APIs that
support JSON.
Consuming JSON on the phone is fairly straightforward. You have two approaches:
• Serialize JSON to and from managed objects (for example, classes).
• Parse JSON much like you would XML.
Before you can handle the JSON, you have to
retrieve it. The network stack can do this for you. For instance, if
you wanted to retrieve statuses from Twitter’s live feed, you could
make a call using the WebClient
class to retrieve the information:
// Create the client
WebClient client = new WebClient();
// Handle the event
client.DownloadStringCompleted += (s, e) =>
{
// Make sure the process completed successfully
if (e.Error == null)
{
// Retrieve the JSON
string jsonString = e.Result;
}
};
// Start the execution
string api =
"http://api.twitter.com/1/statuses/public_timeline.json";
client.DownloadStringAsync(new Uri(api));
Retrieving data from services that expose their
data via JSON is like any other type of network request; therefore, you
can use the WebClient
class to accomplish this. As this example shows, you can use the DownloadString
API to retrieve JSON data.
Although the phone does have built-in support for serializing objects to and from JSON3,
the current version is not very forgiving. In both of these cases, I
suggest you look at using an open-source library that has existed for
.NET for quite a while and supports Windows Phone: Json.NET.
Using JSON Serialization
The Json.NET libraries support serialization through a class called JsonConvert,
which enables you to serialize and deserialize objects to and from JSON. Passing in an object to JsonConvert
’s SerializeObject
method will return a string made up of JSON:
var guy = new Person()
{
FirstName = "Shawn",
LastName = "Wildermuth",
BirthDate = new DateTime(1969, 4, 24)
};
string json = JsonConvert.SerializeObject(guy);
// Returns:
// {
// "FirstName":"Shawn",
// "LastName":"Wildermuth",
// "BirthDate":"\/Date(-21758400000-0400)\/"
// }
The SerializeObject
method takes a
single object but will serialize an entire object tree or collection.
The passed-in object is the start of the serialization. To reverse the
process, you would use the generic DeserializeObject
method:
string theJson = @"{
""FirstName"":""Shawn"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/""
}";
Person recreated = JsonCon
vert.DeserializeObject<Person>(theJson);
Put this together with the WebClient
class and you can interact with JSON-powered services (usually REST
services). For example, to call Twitter to get the latest tweets in the
public feed, you can call a REST interface like so:
// Create the client
WebClient client = new WebClient();
// Handle the event
client.DownloadStringCompleted += (s, e) =>
{
// Make sure the process completed successfully
if (e.Error == null)
{
// Use the result
string jsonString = e.Result;
Tweet[] tweets =
JsonConvert.DeserializeObject<Tweet[]>(jsonString);
tweetList.ItemsSource = tweets;
}
};
// Start the execution
string req =
"http://api.twitter.com/1/statuses/public_timeline.json";
client.DownloadStringAsync(new Uri(req));
By using the WebClient
class’s
networking to request data from Twitter, the JSON returned can be
turned into objects directly. When you use Json.NET’s DeserializeObject
method, it is smart enough to try to match the fields of the managed
object to the JSON properties. It does not require that the entire
object graph in JSON be modeled as classes. This means that when it
maps the JSON object to your class, it will include only properties
that both your class and the JSON object have in common.
Parsing JSON
Another option with dealing with JSON is to
parse it and consume it without having to create classes to hold the
data. The Json.NET library includes a couple of classes that simplify
this: JObject
and JArray
. For instance, to read a simple object, just call the JObject
class’s Parse
method:
string theJson = @"{
""FirstName"":""Shawn"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/""
}";
JObject jsonObject = JObject.Parse(theJson);
string firstName = jsonObject["FirstName"].Value<string>();
string lastName = jsonObject["LastName"].Value<string>();
DateTime birthDate = jsonObject["BirthDate"].Value<DateTime>();
Parsing the JSON into an instance of JObject
gives you first-class access to the properties of the JSON. You can retrieve the specific values in the JSON by using the Value
method. The generic version of the Value
method lets you specify the type of data you expect to retrieve, as shown previously.
Although this example shows you a simple object, you can retrieve collections in JSON as well. The JArray
class is how you parse and consume JSON collections:
string jsonCollection = @"[
{
""FirstName"":""Shawn"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/""
},
{
""FirstName"":""Pennie"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/""
},
]";
JArray jsonArray = JArray.Parse(jsonCollection);
foreach (JObject item in jsonArray)
{
string firstName = item["FirstName"].Value<string>();
string lastName = item["LastName"].Value<string>();
DateTime birthDate = item["BirthDate"].Value<DateTime>();
}
Because the JSON being parsed here is an array of items (note the square bracket that starts the JSON), the JArray
class will parse this into an array of JObject
items, as shown here. This also means that JSON objects that contain collections will enable you to iterate through them as JArray
objects, like so:
string json = @"{
""FirstName"":""Shawn"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/"",
""FavoriteGames"" :
[
{
""Name"" : ""Halo 3"",
""Genre"" : ""Shooter""
},
{
""Name"" : ""Forza 2"",
""Genre"" : ""Racing""
},
]
}";
JObject guy = JObject.Parse(json);
string firstName = guy["FirstName"].Value<string>();
string lastName = guy["LastName"].Value<string>();
DateTime birthDate = guy["BirthDate"].Value<DateTime>();
foreach (JObject item in guy["FavoriteGames"])
{
string name = item["Name"].Value<string>();
string genre = item["Genre"].Value<string>();
}
Although the object being parsed is a JObject
, the collection in the JSON (accessed via the indexer, like any other field) would yield a JArray
object (that can be iterated through). In this way, you can go through a complex object graph in JSON as needed.
The Json.NET library even lets you execute LINQ queries against parsed JSON. You can do this by writing LINQ queries over JArray
objects, like so:
string json = @"[
{
""FirstName"":""Shawn"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/"",
""FavoriteGames"" :
[
{
""Name"" : ""Halo 3"",
""Genre"" : ""Shooter""
},
{
""Name"" : ""Forza 2"",
""Genre"" : ""Racing""
},
]
},
{
""FirstName"":""Pennie"",
""LastName"":""Wildermuth"",
""BirthDate"":""\/Date(-21758400000-0400)\/"",
""FavoriteGames"" : []
},
]";
JArray people = JArray.Parse(json);
var qry = from p in people
where p["FavoriteGames"].Count() > 0
select p;
var peopleWithGames = qry.ToArray();
In this example the query is searching for people who have at least one item in their FavoriteGames
field in the JSON. This returns the raw Json.NET objects so that you
can continue to pull out the data manually as shown in earlier
examples, though you can use LINQ to project into classes as well. This
technique is commonly used when you want to query the JSON result but
end up with a subset of the JSON results as managed objects.
The major reason you want to end up with
managed objects (such as instances of classes) is to use data binding.
Although data binding can work with JObject
objects,
there is no way to have the name in a binding element do a lookup into
the field of a JSON object. In most cases, you will need to project
these into managed objects like so:
// Create the client
WebClient client = new WebClient();
// Handle the event
client.DownloadStringCompleted += (s, e) =>
{
// Make sure the process completed successfully
if (e.Error == null)
{
JArray tweets = JArray.Parse(jsonString);
var qry = from t in tweets
where t["source"].Value<string>() == "web"
let username = t["user"]["screen_name"].Value<string>()
orderby username
select new Tweet()
{
Text = t["text"].Value<string>(),
User = new User()
{
Screen_Name = username,
}
};
tweetList.ItemsSource = qry.ToList();
}
};
// Start the execution
string req =
"http://api.twitter.com/1/statuses/public_timeline.json";
client.DownloadStringAsync(new Uri(req));
By projecting into a class structure,
you can end up with classes that easily handle data binding directly to
XAML objects. This approach is often better than using serialization
because you might need only a subset of the result of the data you
retrieved from a REST-based interface. This way, you can use LINQ to
filter, sort, and shape your data into objects that better represent
what you want to use on the phone.