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. 😉