Deserializing awkward JSON using Json.NET

Aside: I'm never quite sure whether every instance of a word which has a Z in it, that sounds like it could have an S in it, should have an S in it when it's en-gb instead of en-us. In this instance I've opted to stick with a Z as that's what the common usage of it (i.e. for coders!) will be.

I answered a question on Stack Overflow yesterday on the subject of deserializing some JSON that doesn't immediately lend itself to being thrown straight through Json.NET and coming out the other side in just the form the person wants. Their main issue was that (bar one field name that has spaces in) they had several fields, all beginning with the string "Testname" and then a number which they wanted to map to a Dictionary<string, object>. As this was the only non-compliant data in their JSON, I suggested using JsonExtensionDataAttribute to dump all the values that don't map 1-2-1 to a property of their class into the dictionary. For this scenario, along with a little bit of code to ignore items that don't match the pattern, that works.

One thing I forgot to suggest, that I've used before, is a custom JsonConverter. These allow you to define the behaviour that you want for a given type when converting to/from JSON. In this example, the code is actually more verbose than using the attribute (and also required adding an attribute to the class so that Json.NET knows to use the converter!

The code for the JsonConverter implementation is (and doubtless could be better!):

public sealed class KeuringRegelConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(KeuringRegel).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var instance = new KeuringRegel
        {
            testNames = new Dictionary<string, object>()
        };

        // Take care of what can be bound easily, see http://blog.maskalik.com/asp-net/json-net-implement-custom-serialization/
        var jsonObject = JObject.Load(reader);
        serializer.Populate(jsonObject.CreateReader(), instance);

        // Deal with the special cased stuff
        foreach (var child in jsonObject.Children())
        {
            if (child is JProperty)
            {
                var property = child as JProperty;
                if (property.Name.StartsWith("testname", StringComparison.OrdinalIgnoreCase))
                {
                    instance.testNames.Add(property.Name, property.Value);
                }
                if (property.Name.Equals("Apparaat naam", StringComparison.OrdinalIgnoreCase))
                {
                    instance.Apparaatnaam = property.Value.ToString();
                }
            }
        }
        return instance;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

This also (as I mentioned above) requires the class that the data is being deserialized into to be annotated with the JsonConverter attribute:

[JsonConverter(typeof(KeuringRegelConverter))]
public class KeuringRegel
{
    public string Projekt { get; set; }
    public string Ruimte { get; set; }
    public Apparaat Apparaat { get; set; }
    public string Apparaatnaam { get; set; }
    public string Status { get; set; }
    public Dictionary<string, object> testNames { get; set; }
}


No Comments

Add a Comment