/ C#

Detecting mismatched objects with Newtonsoft.Json

One common problem with API Client Libraries is that their strongly typed objects don't match the response objects that the API gives. I faced this issue with TMDbLib and wanted to solve it in my unit tests.

It turns out that we relatively easily can work in a "validator" using the JsonSerializerSettings object given to a JsonSerializer.

The settings

We use the MissingMemberHandling and ContractResolver properties of the JsonSerializerSettings in addition to the Error event handler. Like this:

private static readonly List<ErrorEventArgs> Errors = new List<ErrorEventArgs>();

public static void Main()
{
	JsonSerializerSettings settings = new JsonSerializerSettings
	{
		MissingMemberHandling = MissingMemberHandling.Error,
		ContractResolver = new FailingContractResolver(),
		Error = Error
	};

	JsonSerializer serializer = JsonSerializer.Create(settings);
}

private static void Error(object sender, ErrorEventArgs errorEventArgs)
{
	Errors.Add(errorEventArgs);
	errorEventArgs.ErrorContext.Handled = true;
}

In the above code, we set up a settings object that specifies that any missing C# properties (MissingMemberHandling) are errors. This ensures that if our object is missing a property in order for the JSON to completely deserialize, we will be notified.

Note that the Error handler is special. In it, we record any error for later, in addition to marking the error as "handled". Marking it as handled makes sure that Newtonsoft.Json doesn't prematurely throw an exception (It would suck having to uncover one error at a time).

It also sets up a ContractResolver which we've made, that ensures that any C# property we have, will always require a counterpart in the JSON.

public class FailingContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty res = base.CreateProperty(member, memberSerialization);

        if (!res.Ignored)
            // If we haven't explicitly stated that a field is not needed, we require it for compliance
            res.Required = Required.AllowNull;

        return res;
    }
}
Using it

All that is left now is to actually use the JsonSerializer we've created. Calling Serialize or Deserialize with mismatched objects will lead you to the Error handler, where you in turn can react on the stored events. I find this extremely useful in unit tests.

Example code

I've put the source code for the examples above online here: github.com/LordMike/blog-examples/detect-mismatched-objects-newtonsoft.

Example

Seeing it in action

All this is in use in the TMDbLib tests. You can find the juicy parts here:

All the test classes inherit from the TestBase class. In it, we find a client library already instantiated for the tests, which is then used everywhere. When a test is about to end, the TestBase object is disposed, and then throws an appropriate exception if needed.

In the class you'll also find methods to ignore certain errors, as TMDb isn't always straightforward.