WinRT JSON Parser

WinRT JSON Parser

The classes in Windows.Data.Json are no fun to use. This handy class makes it better.

The new WinRT libraries generally make a lot of things easier, faster, and better, but one area that seems harder to work with is JSON data. The classes in the Windows.Data.Json namespace are simple, but verbose to use. Consider this example, using the following simple JSON:

[{
    "identities": {
        "google": {
            "userId": "Google:101303958185943883774",
            "accessToken": "ya29.AHES6ZRqPjKgoFjlp7FeJUK56ca19HzqIlzQQSLIj",
            "isValid": true,
            "age": 38
        }
    }
}]

To parse this using the built-in classes, it might look like this:

var obj = new Windows.Data.Json.JsonObject();
if (Windows.Data.Json.JsonValue.TryParse(json, out obj))
{
    var identities = obj.GetArray()[0].GetObject();
    var identity = identities["identities"].GetObject()["google"].GetObject();
    var token = identity["accessToken"].GetString();
    var isValid = identity["isValid"].GetBoolean();
    var age = identity["age"].GetNumber();
}

And if you add in checking to see if the properties exist first, the code is even longer. It isnt horrible, but it isnt how we like to deal with JSON normally.

There are great open-source tools out there like the venerable Json.NET that make like easier. But sometimes taking a dependency on yet another third-party library isnt desirable or possible. Something so fundamental like JSON parsing should be as easy and painless as possible.

So what if you could just do this:

dynamic arr = JsonParser.Parse(json);
var identity = arr[0].identities.google;
var token = identity.accessToken;
var isValid = identity.isValid;
var age = identity.age;

That looks nicer and much more inline with how JSON is normally treated in Javascript. The complete code for JsonParser is below - it is just a single small C# class that you can include in your project with no other dependencies. The secret sauce is in the ConvertObject() method; by leveraging a dynamic ExpandoObject, we can access the properties directly. The resulting object also provides a Contains() method so you can easily check for the existence of the property before accessing it.

I am finding this much easier and more intuitive to use, so I thought I would share it. Let me know if you try it out and how you like it.

JsonParser.cs source:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.Data.Json;

namespace Monsters.WindowsStore.Implementations
{
    public class JsonParser
    {
        public static Task<dynamic> ParseAsync(string json)
        {
            return Task.Run(() =>
                {
                    return Parse(json);
                });
        }

        public static Task<dynamic> ParseAsync(IJsonValue json)
        {
            return Task.Run(() =>
                {
                    return Parse(json);
                });
        }

        public static dynamic Parse(string json)
        {
            var j = Windows.Data.Json.JsonValue.Parse(json);
            return Parse(j);
        }

        public static dynamic Parse(IJsonValue json)
        {
            dynamic obj = Convert(json);
            return obj;
        }

        static dynamic Convert(IJsonValue json)
        {
            dynamic obj = null;
            switch (json.ValueType)
            {
                case JsonValueType.Array:
                    obj = ConvertArray(json.GetArray());
                    break;
                case JsonValueType.Boolean:
                    obj = json.GetBoolean();
                    break;
                case JsonValueType.Null:
                    obj = null;
                    break;
                case JsonValueType.Number:
                    obj = json.GetNumber();
                    break;
                case JsonValueType.Object:
                    obj = ConvertObject(json.GetObject());
                    break;
                case JsonValueType.String:
                    obj = json.GetString();
                    break;
            }
            return obj;
        }

        static dynamic ConvertArray(JsonArray jsonArray)
        {
            dynamic[] items = new dynamic[jsonArray.Count];
            for (int i = 0; i < jsonArray.Count; i++)
            {
                items[i] = Convert(jsonArray[i]);
            }
            return items;
        }

        static dynamic ConvertObject(JsonObject jsonObject)
        {
            dynamic obj = new System.Dynamic.ExpandoObject();
            var d = (IDictionary<string, object>)obj;

            obj.Contains = new Func<string, bool>((prop) =>
                {
                    return d.ContainsKey(prop);
                });
            obj.Get = new Func<string, dynamic>((prop) =>
                {
                    return d[prop];
                });

            List<string> keys = new List<string>();
            foreach(var key in jsonObject.Keys)
            {
                keys.Add(key);
            }

            int i = 0;
            foreach(var item in jsonObject.Values)
            {
                d.Add(keys[i], Convert(item));
                i++;
            }
            return obj;
        }
    }
}