I already mentioned how I’m working on a big data integration project. Sadly, one of the systems involved exposes what I would describe as a “less-than-ideal” API. It’s buggy, poorly-documented… I could go on, but probably shouldn’t. 😃

But the big thing that got me was the lack of consistency. Not just across endpoints, but even within the same model on a single endpoint!

Fortunately, JSON.NET provides ways of dealing with these sorts of no-good-very-bad APIs in .NET. Let’s look at one example of how!

[more]

So, here I am, trying to pull data from this 3rd party API. I’m hitting a search endpoint in an attempt to see if a matching record already exists. Normally, the response looks something like this:

{
    "results": {
        "matches": [
            {
                "firstName": "John",
                "lastName": "Doe"
            },
            {
                "firstName": "Jane",
                "lastName": "Doe"
            }
        ]
    }
}

I’m consuming this API from .NET and using JSON.NET to deserialize things into POCOs. The POCOs look like this:

public class SearchModel 
{
    public ResultModel Results { get; set; }
}

public class ResultModel 
{
    public Match[] Matches { get; set; }
}

public class Match 
{
    public string FirstName { get; set; }    

    public string LastName { get; set; }    
}

Seems pretty simple, right?

WRONG!

Consistency… what’s that?

It turns out that this API actually return data in a slightly different shape sometimes. If there is exactly one match, the response looks like this:

{
    "results": {
        "matches": 
            {
                "firstName": "Jon",
                "lastName": "Snow"
            }
    }
}

Do you see the subtle difference? That’s right, the matches property is now an object, not an array!

Sidenote: If you are building APIs, DO NOT DO THIS. There is no language, that I’m aware of, where this type of inconsistency will be nice to work with. Sure, some languages make it easier to handle, but no matter the language, you’re forcing them to check the data, determine if it’s an array or an object, then handle it accordingly. BE CONSISTENT! I’ll get off my soapbox now… 😃

So, as you can probably imagine, this alternate, no-good-very-bad response does not deserialize correctly, as JSON.NET is looking to deserialize an array, but will instead find an object. Fun!

Sadly, other APIs do this, too…

I had a pretty good idea of what I wanted to do: plug a custom converter into JSON.NET, and have it handle both a single object and an array of objects. And it turns out that I’m not the first person to need to do this! Here are just a couple of the Stack Overflow answers I reviewed while trying to determine the best way to accomplish this:

How to handle both a single item and an array for the same property using JSON.net

Serialize a Json property that is sometimes an array

Building a Custom Converter

Now that I’d confirmed that I was on the right track, I quickly coded up a converter:

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

    public override bool CanWrite
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            return new[]
            {
                serializer.Deserialize<Match>(reader)
            };
        }
        else
        {
            return serializer.Deserialize<Match[]>(reader);
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Match[]);
    }
}

The interesting bits are all in the ReadJson method, so let’s break it down:

First, we do a quick check to see if we are dealing with an object. If not, we can safely assume it’s the start of an array…

if (reader.TokenType == JsonToken.StartObject)
{
    //The response returned an object, not an array. >:(
    ...
}
else
{
    //The response actually contains an array.
    ...
}

If we receive an object, we want to wrap it in an array after we deserialize it:

    return new[]
    {
        serializer.Deserialize<Match>(reader)
    };

I want you to notice two things here. First, we’re using the serializer that was passed in to our ReadJson method. Second, we’re not passing in a string or anything like that. We’re just handing off the same reader that we received, and telling serializer to take a Match object out of that.

If we’re looking at an array, then we really don’t need to do anything special, we just ask serializer to do its thing:

    return serializer.Deserialize<Match[]>(reader);

As you can see, there really wasn’t much to the converter. And wiring it up is just as easy. All we have to do is decorate the target property on our model…

public class ResultModel 
{
    [JsonConverter(typeof(MatchOrArrayConverter))]
    public Match[] Matches { get; set; }
}

Now our code will deserialize either variation of response from the no-good-very-bad API endpoint automagically.

Closing Thoughts

When I first discovered that this no-good-very-bad API was returning data inconsistently, I feared I’d have to do something un-fun, like manually work with the JSON. Fortunately, JSON.NET was built to handle crazy situations like this, and working around the issue was easy.

JSON.NET is one of my favorite .NET libraries. It works great out of the box, but when you need to customize it, you can do so progressively, without having to reinvent the wheel or reimplement a lot of base class logic.

Thank you, JSON.NET, for being awesome! 😄