May 2013

Volume 28 Number 05

JSON - Parse JSON Strings in Windows Runtime Components

By Craig Shoemaker | May 2013

Windows Store apps built with JavaScript let anyone with HTML and JavaScript skills build native Windows apps, but JavaScript isn’t always the best choice to solve every problem. Some behavior in your apps might be better implemented in a more object-oriented fashion using C#, Visual Basic or C++. Also, certain aspects of your code might be a candidate for reuse among multiple Windows Runtime (WinRT) components that require data from the UI layer. Passing data from JavaScript into WinRT components and back to the UI is important to understand in either of these situations.

On the Web, data is often passed from the client to the server and back in the form of JSON objects. In different contexts, frameworks like ASP.NET Web Forms and ASP.NET MVC include features such as model binders or at least some sort of “auto-magic” handling on the server side for parsing JSON objects. WinRT components have objects with affordances for parsing JSON, but the support is low-level, and more streamlined interaction requires some explicit handling on your part.

This article demonstrates how to reliably parse JSON strings passed into WinRT components in order to hydrate strongly typed objects and return a result back up to the UI.

Restrictions for Interacting with WinRT components

Before discussing the particulars of parsing JSON objects, you must first get acquainted with requirements and restrictions for interacting with WinRT components. The MSDN help topic, “Creating Windows Runtime Components in C# and Visual Basic” (bit.ly/WgBBai), details what’s needed for WinRT components in regard to declaring method parameter and return types. Allowed types are largely composed of primitive types and a handful of collection types, so attempting to pass a raw JSON object into a component isn’t allowed. The best way to pass a JSON object into a managed component is to first serialize the JSON object (using the JSON.stringify method), as strings are fully supported in these classes.

Parsing JSON Objects in Managed Code

The Windows.Data.Json namespace includes a number of different classes designed to work with JSON objects in a strongly typed manner, including the JsonValue, JsonArray and JsonObject classes. The JsonValue class represents a JSON value that’s exposed in the form of a string, number, Boolean, array or object (see more on this at bit.ly/14AcTmF). Parsing a JSON string requires that you pass the raw string to JsonValue, which then is able to return an instance of JsonObject.

The JsonObject class represents a full JSON object and includes methods to manipulate the source object. Through the JsonObject class, you can add and remove members, extract data from members, iterate over each member and even serialize the object again. More details about JsonObject are available at bit.ly/WDWZkG.

The JsonArray class represents a JSON array, which, again, includes a host of methods for controlling the array, such as iteration and addition and removal of array elements. More information about the interface of the JsonArray class is available at bit.ly/XVUZo1.

As an example to see how to begin using these classes, consider the following JSON object in JavaScript:

{
  firstName: "Craig"
}

Before attempting to pass this object to a WinRT component, you must serialize the object into a string using the JSON.stringify function. Notice what happens after the object is serialized—the very same object is represented as follows:

"{
  '_backingData': {
    'firstName': 'Craig'
  },
  'firstName': 'Craig',
  'backingData': {
    'firstName':'Craig'}
}"

This might come as a surprise to you, as the very same function call in a Web browser simply serializes the object into a string without adding any members to the object. This change in the structure of the JSON string affects how you extract data from the object.

The first step in reading this data in a WinRT component is to attempt to parse the incoming string as a JsonValue instance. If that parse succeeds, then you can request the JsonObject from the root JsonValue instance. In this case the JsonValue is the root object as created by the call to the stringify function, and JsonObject grants you access to the original object you started with in JavaScript.

The following code depicts how, once the JsonObject is available, you can use the GetNamedString method to extract the value of the “firstName” member into a variable:

JsonValue root;
JsonObject jsonObject;
string firstName;
if (JsonValue.TryParse(jsonString, out root))
{
  jsonObject = root.GetObject();
  if (jsonObject.ContainsKey("firstName"))
  {
    firstName = jsonObject.GetNamedString("firstName");
  }
}

A similar approach is used to access Boolean and numeric members—where GetNamedBoolean and GetNamedNumber methods are available. The next step is to implement extension methods of the JsonObject in order to make it easy to access JSON data.

Extension Methods for JsonObject

The default implementation of the JsonObject class provides some low-level behavior that’s greatly enhanced with some simple methods that can handle imperfect formatting and can avoid exceptions if members don’t exist in the source. In other words, objects created in JavaScript are bound to encounter formatting or structural problems that could cause exceptions. Adding the following extension methods to the JsonObject class will help mitigate these problems.

The first extension method to add is called GetStringValue. Figure 1 shows the implementation, which first checks to make sure the member exists on the object. In this use, the key parameter is the name of the JSON object property. After the member is known to exist, then the method TryGetValue is used to attempt to access the data from the JsonObject instance. If the value is successfully found, it’s returned as an object implementing the IJsonValue interface.

Figure 1 GetStringValue Extension Method Implementation

public static string GetStringValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  string returnValue = string.Empty;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        returnValue = jsonObject.GetNamedString(key);
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        returnValue = jsonObject.GetNamedNumber(key).ToString();
      }
      else if (value.ValueType == JsonValueType.Boolean)
      {
        returnValue = jsonObject.GetNamedBoolean(key).ToString();
      }
    }
  }
  return returnValue;
}

The IJsonValue interface includes the read-only ValueType property, which exposes the given value of the JsonValueType enumeration that signifies the object’s data type. After interrogating ValueType, the appropriately typed method is used to extract the data from the object.

The GetStringValue method includes awareness of Booleans and numeric values in order to guard against malformed JSON objects. You might choose to make your implementation stricter and forgo parsing or throw an error if the JSON object isn’t strictly formatted for the expected type, but the code in my example makes the parse operation flexible and guards against errors.

The next extension method, seen in Figure 2, is the implementation to extract Boolean values. In this case, Boolean values expressed as strings (for example, “1” or “true” for a true value, and so on) and numbers (for example, “1” for true and “0” or false) are supported in the GetBooleanValue method.

Figure 2 GetBooleanValue Extension Method Implementation

public static bool? GetBooleanValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  bool? returnValue = null;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        string v = jsonObject.GetNamedString(key).ToLower();
        if (v == "1" || v == "true")
        {
          returnValue = true;
        }
        else if (v == "0" || v == "false")
        {
          returnValue = false;
        }
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        int v = Convert.ToInt32(jsonObject.GetNamedNumber(key));
        if (v == 1)
        {
          returnValue = true;
        }
        else if (v == 0)
        {
          returnValue = false;
        }
      }
      else if (value.ValueType == JsonValueType.Boolean)
      {
        returnValue = value.GetBoolean();
      }
    }
  }
  return returnValue;
}

The numeric-based extension methods are set up to return nullable types, so in this case, GetDoubleValue returns a nullable double. The corrective behavior in this case attempts to convert strings into the possible corresponding numeric values (see Figure 3).

Figure 3 GetDoubleValue Extension Method Implementation

public static double? GetDoubleValue(this JsonObject jsonObject, 
  string key)
{
  IJsonValue value;
  double? returnValue = null;
  double parsedValue;
  if (jsonObject.ContainsKey(key))
  {
    if (jsonObject.TryGetValue(key, out value))
    {
      if (value.ValueType == JsonValueType.String)
      {
        if (double.TryParse(jsonObject.GetNamedString(key), 
          out parsedValue))
        {
          returnValue = parsedValue;
        }
      }
      else if (value.ValueType == JsonValueType.Number)
      {
        returnValue = jsonObject.GetNamedNumber(key);
      }
    }
  }
  return returnValue;
}

Because the built-in method to extract numbers in the JsonObject class returns a double, and often data values are expressed as integers, the following code shows how the GetIntegerValue method wraps the GetDoubleValue method and converts the result into an integer:

public static int? GetIntegerValue(this JsonObject jsonObject, 
  string key)
{
  double? value = jsonObject.GetDoubleValue(key);
  int? returnValue = null;
  if (value.HasValue)
  {
    returnValue = Convert.ToInt32(value.Value);
  }
  return returnValue;
}

Adding Factory Support

Now that the JsonObject class is extended to include some higher-­level support for data extraction into primitive types, the next step is to use this support in factory classes that are responsible for taking incoming JSON strings and returning an instance of a hydrated domain object.

The following code depicts how a person is modeled in the system:

internal class Person
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public bool? IsOnWestCoast { get; set; }
}

The following code shows the Create method in the Person­Factory class that accepts a string:

 

public static Person Create(string jsonString)
{
  JsonValue json;
  Person person = new Person();
  if (JsonValue.TryParse(jsonString, out json))
  {
    person = PersonFactory.Create(json);
  }
  return person;
}

Figure 4 shows a Create method that accepts a JsonValue. These Create methods used together are responsible for taking in a raw string and returning an instance of the Person class with the expected data in each member. The methods are separated and overloaded in order to provide support for JSON arrays, which is explained in the next section.

Figure 4 PersonFactory Create Method That Accepts a JsonValue

public static Person Create(JsonValue personValue)
{
  Person person = new Person();
  JsonObject jsonObject = personValue.GetObject();
  int? id = jsonObject.GetIntegerValue("id");
  if (id.HasValue)
  {
    person.Id = id.Value;
  }
  person.FirstName = jsonObject.GetStringValue("firstName");
  person.LastName = jsonObject.GetStringValue("lastName");
  bool? isOnWestCoast = jsonObject.GetBooleanValue("isOnWestCoast");
  if (isOnWestCoast.HasValue)
  {
    person.IsOnWestCoast = isOnWestCoast.Value;
  }
  return person;
}

Adding Array Support

Sometimes your data comes in the form of object arrays rather than just single objects. In this case you must attempt to parse the string as an array by leveraging the JsonArray class. Figure 5 shows how the incoming string is parsed into an array and then each item is passed to the Create method for final parsing into the model. Notice that a new instance of the Person list is first created so that in case the string doesn’t parse into an object array, the result is an empty array, which helps avoid unexpected exceptions.

Figure 5 PersonFactory CreateList Method

public static IList<Person> CreateList(string peopleJson)
{
  List<Person> people = new List<Person>();
  JsonArray array = new JsonArray();
  if (JsonArray.TryParse(peopleJson, out array))
  {
    if (array.Count > 0)
    {
      foreach (JsonValue value in array)
      {
        people.Add(PersonFactory.Create(value));
      }
    }
  }
  return people;
}

Adding Support Classes

The next step is to create an object that’s responsible for using the factory class and doing something interesting with the resulting model instances. Figure 6 demonstrates how both individual and JSON array strings are consumed and then manipulated as strongly typed objects.

Figure 6 ContactsManager Implementation (Without Async Support)

using System.Collections.Generic;
public sealed class ContactsManager
{
  private string AddContact(string personJson)
  {
    Person person = PersonFactory.Create(personJson);
    return string.Format("{0} {1} is added to the system.",
      person.FirstName,
      person.LastName);
  }
  private string AddContacts(string personJson)
  {
    IList<Person> people = PersonFactory.CreateList(personJson);
    return string.Format("{0} {1} and {2} {3} are added to the system.",
      people[0].FirstName,
      people[0].LastName,
      people[1].FirstName,
      people[1].LastName);
  }
}

Supporting Asynchronous Interaction

Calls of this nature into methods of WinRT components should be done asynchronously, as the JSON messages have the potential to grow to an arbitrary size, which might introduce latency into your application.

The following code includes the method added to the ContactsManager to support asynchronous access to the AddContact method:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Foundation;
public IAsyncOperation<string> AddContactAsync(string personJson)
{
  return Task.Run<string>(() =>
  {
    return this.AddContact(personJson);
  }).AsAsyncOperation();
}

The AddContactAsync method accepts the JSON string and then starts a Task, which does the work of running the AddContact method. Once the Task is complete, a response is sent up to the JavaScript promise as facilitated by the IAsyncOperation interface support. The full source of the ContactsManager class with asynchronous support for both AddContact and AddContacts can be found in the accompanying code download.

Keeping Promises in JavaScript

The final piece of the puzzle is to use the ContactsManager class in JavaScript and invoke calls to the class using the promise pattern. The approach used in this example is to implement a view model that passes the modeled data to the WinRT component and then waits for a response. The data used to pass to the component is defined in Figure 7, which includes a single JSON object as well as an array.

Figure 7 JSON Data Source

var _model = {
  contact: {
    id: 1000,
    firstName: "Craig",
    lastName: "Shoemaker"
  },
  contacts: [
    {
      id: 1001,
      firstName: "Craig",
      lastName: "Shoemaker",
      isOnWestCoast: "true"
    },
    {
      id: 1002,
      firstName: "Jason",
      lastName: "Beres",
      isOnWestCoast: "0"
    }
  ]
}

The view model, as seen in Figure 8, includes a member for the model and members for messages that are returned from the WinRT component. The Windows Library for JavaScript (WinJS) binding framework is used to bind messages returned from the response to the HTML elements. A full listing of the page module is available in the accompanying code download so you can see how all the individual parts fit together.

Figure 8 View Model That Uses ContactsManager

var _vm = {
  ViewModel: WinJS.Binding.as({
    model: _model,
    contactMsg: "",
    contactsMsg: "",
    addContact: function () {
      var mgr = ParseJSON.Utility.ContactsManager();
      var jsonString = JSON.stringify(_vm.ViewModel.model.contact);
      mgr.addContactAsync(jsonString).done(function (response) {
        _vm.ViewModel.contactMsg = response;
      });
    },
    addContacts: function () {
      var mgr = ParseJSON.Utility.ContactsManager();
      var jsonString = JSON.stringify(_vm.ViewModel.model.contacts);
        mgr.addContactsAsync(jsonString).done(function (response) {
          _vm.ViewModel.contactsMsg = response;
        });
      }
  })
};

Note that if you want to bind the add­Contact or addContacts functions to a button during data binding, you must run the WinJS.Utilities.requireSupportedForProcessing function, passing in a reference to the function on your view model.

The final step is to add the appropriate elements and attributes to the HTML to support binding. A div element acts as the main binding container for the binding elements and is marked by setting data-win-bindsource=“Application.Pages.Home.View­Model.” Then the header elements are bound to their data members by supplying the appropriate values to the data-win-bind attributes:

<section aria-label="Main content" role="main">
  <div data-win-bindsource=
    "Application.Pages.Home.ViewModel">
    <h2 data-win-bind="innerText: contactMsg"></h2>
    <hr />
    <h2 data-win-bind="innerText: contactsMsg"></h2>
  </div>
</section>

So there you have it! Building Windows Store apps with JavaScript gives you an opportunity to leverage your existing skills from the Web to build native, modern UI apps, but there are a number of distinguishing factors between the two platforms. Low-level support for parsing JSON data is available through the Windows.Data.Json namespace, but you can add richer support with a few extensions to existing objects.


Craig Shoemaker is a software developer, podcaster, blogger and technical evangelist. He’s also a Code Magazine, MSDN and Pluralsight author. In his spare time, he enjoys looking for a haystack in which to hide his prized needle collection. You can reach him on Twitter at twitter.com/craigshoemaker.

Thanks to the following technical experts for reviewing this article: Christopher Bennage (Microsoft), Kraig Brockschmidt (Microsoft) and Richard Fricks (Microsoft)
Christopher Bennage is a developer at Microsoft on the Patterns & Practices team. His job is to discover, collect and encourage practices that bring developers joy. Amongst his recent technical interests are JavaScript and (casual) game development. He blogs at dev.bennage.com.

Kraig Brockschmidt has worked with Microsoft since 1988, focusing on helping developers through writing, education, public speaking and direct engagement. He’s a senior program manager on the Windows Ecosystem team working with key partners on building Windows Store apps and bringing knowledge gained in that experience to the wider developer community. His most recent book is "Programming Windows 8 Apps in HTML, CSS, and JavaScript" (a free ebook from Microsoft Press); he blogs at kraigbrockschmidt.com/blog.

Richard Fricks has worked with the development community for the past 20 years, most recently helping design the Windows Runtime API strategy for Media and assisting the development community in adopting new Windows 8 features. He’s currently a program manager on the Windows Scenario Adoption Team.