Json Converter/Resolver Performance Comparison


Assuming you’re dealing with a legacy software system written in C# ages ago, it communicates with various APIs, all featuring a common functionality: logging. Now, the issue arises – how to avoid logging specific fields within incoming messages? Some of these fields might contain sensitive data like passwords or GDPR-related information. The contemporary solution likely lies in Microsoft.Extensions.Compliance.Redaction library and its suite of features.

Hold on – did I say „contemporary”? Let’s rewind and start from scratch. You’re dealing with an old, legacy system equipped with its custom logging mechanism. There’s no room for the word „contemporary” here.

Instead of daydreaming, let’s get to work. What’s the simplest approach? Perhaps modifying the serializer responsible for dumping objects into the logs (let’s say it’s the JsonSerializer from the Newtonsoft.Json library) to avoid dumping certain properties of the objects. There are at least two methods to achieve this:

  1. Write a converter that inspects objects being serialized on the fly.
  2. Write a contract resolver to accomplish the same task.

At first glance, even with a clear description of both methods like the one found here, it can be challenging to decide which one to choose. However, diving into the internal structure of both constructs reveals that converters contain a method called CanConvert. This can be invaluable if we’re planning to serialize numerous objects and performance is a key consideration.

Thanks to my colleague, Łukasz, I can present a tidy, concise comparison of both approaches, prepared using BenchmarkDotNet. Although I had heard of this performance measurement framework before, it was Łukasz who showed me how easy it is to use. Below is the complete testing solution, just copy-paste and run it to see the results.

How does it look after running? The test results speak for themselves. Serialization without any alterations takes almost one millionth of a second. After adding a converter, the time extends sixfold, and switching to a Resolver… over six hundredfold. The difference is staggering, and even though I anticipated it, the magnitude remains a mystery. Is reflection really that inefficient?

Benchmark results. The difference between converter and resolver performances is colossal.

If we wanted to further speed up the converter for a larger number of different classes adorned with the same interface, IContainSensitiveData, there’s nothing simpler than adding two dictionaries where types of already processed objects would be saved:

The moral of all this is that it’s worth examining proposed solutions not only for correctness but also for performance. Especially if it relies on reflection.

5 1 vote
Article Rating
Powiadom o
0 komentarzy
Inline Feedbacks
View all comments