I found myself looking for an easy way to deserialise Json strings to dynamic objects today. Of course there are many excellent libraries (like Json.net) that will already do this for you. The problem is that I need to do it in a Metro Style app, which can't reference regular .Net libraries.
So, what to do?
First off, Json Serialisation has been provided as a first class citizen in Metro Style apps. There's no point in doing that again. We will use the Windows.Data.Json.JsonObject class provided in WinRT. Where our paths diverge from this provided class, is that the objects are accessed in a relatively gnarly way:
JsonObject jsonObject = JsonObject.Parse(jsonString);
string myStringProperty = jsonObject["myStringPropertyName"].GetString();
double myDoubleProperty = jsonObject["myDoublePropertyName"].GetNumber();
There are different methods you call depending on the type of the value you want. This type is exposed in the ValueType property of the IJsonValue interface, which is the type of the object returned by the indexing operation. We don't necessarily like this.
What I want to do is to read my Json object like this:
dynamic jsonObject = new DynamicJsonObjectReader(jsonString);
string myStringProperty = jsonObject.myStringPropertyName;
double myDoubleProperty = jsonObject.myDoublePropertyName;
It's debatable wether we would want the property to be returned as a concrete type, or as a dynamic object, but for these purposes I want a concrete type for string, double, bool and Array, and another DynamicJsonObjectReader for JsonObject.
To accomplish this, I created an object that wraps up the built-in JsonObject class and inherits from DynamicObject. I called the object a reader, because I've only overridden the TryGetMember method. To make it writable, I would also override the TrySetMember method. This is a bit more complicated, and surplus to my needs. For Now. The ToString method will spit out the Json formatted representation, which at the moment is useful for debugging. Once TrySetMember is properly implemented, ToString would be used for serialising. Here is the code:
Update:I've added an implementation for GetDynamicMemberNames. This enables the debugger to show you all the members and their values at runtime in the watch window. Pretty handy.
using System;
using System.Dynamic;
using System.Linq;
using Windows.Data.Json;
namespace JsonSerialisation
{
internal class DynamicJsonObjectReader : DynamicObject
{
private readonly JsonObject jsonObject;
public DynamicJsonObjectReader(string jsonString)
{
jsonObject = JsonObject.Parse(jsonString);
}
private DynamicJsonObjectReader(JsonObject jsonObject)
{
this.jsonObject = jsonObject;
}
public override string ToString()
{
return jsonObject.Stringify();
}
public override IEnumerable GetDynamicMemberNames()
{
return jsonObject.Keys;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
IJsonValue jsonValue;
if (!jsonObject.TryGetValue(binder.Name, out jsonValue))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = GetValue(jsonValue);
return true;
}
private object GetValue(IJsonValue jsonValue)
{
object result = null;
switch (jsonValue.ValueType)
{
case JsonValueType.Object:
result = new DynamicJsonObjectReader(jsonValue.GetObject());
break;
case JsonValueType.String:
result = jsonValue.GetString();
break;
case JsonValueType.Number:
result = jsonValue.GetNumber();
break;
case JsonValueType.Boolean:
result = jsonValue.GetBoolean();
break;
case JsonValueType.Array:
result = CreateArray(jsonValue.GetArray());
break;
}
return result;
}
private Array CreateArray(JsonArray jsonArray)
{
return jsonArray.Select(GetValue).ToArray();
}
}
}