A Custom Converter for Json.NET


I was playing around with Json.NET while trying to move some data back and forth between .NET and Flex. I found that I needed to deserialize a string that looks something like:

string json = @"{param1:{FirstName:'Jay',Age:2},param2:{FirstName:'Ray',Age:3}}";

where param1 and param2 are parameters to a method that I want to invoke as a remote service. I have two objects that I want to deserialize within a container object (the outer { }). I really don’t care about the container object but I have no way to tell Json.NET to ignore it. I have to have a real concrete to deserialize the container. I didn’t want to create specific objects for each call being made (there could be a lot) so I created a simple generic object that looks something like the following (error handling removed for clarity):

public class ParameterCollection
{
private readonly Dictionary<string, object> parameters;

internal ParameterCollection(Dictionary<string, object> parameters)
{
this.parameters = parameters;
}

public object this[string name]
{
get { return parameters[name]; }
}

public int Count
{
get { return parameters.Count; }
}
}

In order to deserialize to a ParameterCollection object, I needed to create a converter class that inherits from JsonConverter. It ended up looking like the following (error handling removed for clarity):

public class ParameterCollectionJsonConverter : JsonConverter
{
private readonly Type[] parameterTypes;
private readonly Dictionary<string, object> parameterInstances;

public ParameterCollectionJsonConverter(params Type[] parameterTypes)
{
this.parameterTypes = parameterTypes;
this.parameterInstances = new Dictionary<string, object>(parameterTypes.Length);
}

public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(ParameterCollection));
}

public override object ReadJson(JsonReader reader, Type objectType)
{
reader.Read(); // read past start object token

for (int i = 0; i < parameterTypes.Length; i++)
{
string parameterName = reader.Value as string;

this.parameterInstances.Add(parameterName, new JsonSerializer().Deserialize(reader, parameterTypes[i]));
reader.Read();// read past end object token
}

reader.Read();// read past end object token

return new ParameterCollection(parameterInstances);
}

public static ParameterCollection Deserialize(TextReader jsonTextReader, params Type[] types)
{
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new ParameterCollectionJsonConverter(types));

JsonReader reader = new JsonReader(jsonTextReader);
return serializer.Deserialize(reader, typeof(ParameterCollection)) as ParameterCollection;
}
}

So now I can deserialize to a ParameterCollection by passing in the types of each parameter like so:

TextReader tr = new StringReader(@"{param1:{FirstName:'Jay',Age:2},param2:{FirstName:'Ray',Age:3}}");

ParameterCollection paramCollection = ParameterCollectionJsonConverter.Deserialize(tr, typeof(SomeObject), typeof(SomeObject));

SomeObject someObj1 = paramCollection["param1"] as SomeObject;

I’m sure as soon as I post this somebody will let me know of a built-in way to do the same thing. 😉

Technorati Tags: ,,
A Project Walk Through – Defining User Stories