Newtonsoft.Json .NET - To be, or not to be?⁸

JSON serialization is a big factor in web applications, especially for applications that expose almost all their functionality over REST API as our Virto platform does, and the performance of object serialization or deserialization has a significant impact on overall application performance and responsiveness.

Starting with 2.x Virto platform versions and till to the newest 3.0-rc3 we have been using with Newtonsoft.Json .NET serializer for Web API and basically were happy with it until the last moment, on one of our projects we faced with big API response latency for big size response with 1Mb body size. The average response time was more than 10 seconds!!! and was completely unacceptable.

Digging into this problem and conduct a series of tests we end up on one suspect, it was a JSON serializer - we use Newtonsoft.Json .NET 12.0.3 that integrated into both ASP.NET versions.

Durinng investigations we found this excellent article The Battle of C# to JSON Serializers in .NET Core 3 that contains the benchmarks of six the most popular JSON serializer. But any of time charts from this article doesn’t show the big difference between Newtonsoft.Json and embedded into ASP.NET Core System.Text.Json serializers.

And to confirm our doubts we conducted our own benchmark. We created an empty pure ASP.NET Core Web API project from the templates proposed by Visual Studio 2019. Defined the one API endpoint that returned three responses with different sizes (1.6Mb, 16Mb, and 160Mb) respectively and measured response times for using Newtonsoft.Json and embedded into ASP.NET Core 3.0 System.Text.Json serializers.

Here is our benchmark
image

The benchmark shows that for serialization with using System.Text.Json is 10 times faster than Newtonsoft.Json library.

The answer to the question why the results of our tests for single requests are different from those described in this article, we found later in the same article.

The only surprise here is how poorly Newtonsoft.Json performed. This is probably due to the UTF-16 and UTF-8 issue. The HTTP protocol works with UTF-8 text. Newtonsoft converts this text into .NET string types, which are UTF-16. This overhead is not done in either Utf8Json or System.Text.Json , which work directly with UTF-8.

Conclusions

Does this mean we should all change to System.Text.Json or Utf8Json?
Answer - yes! The System.Text.Json is looking as better replacement due to this library is well supported and Microsoft will continue to invest resources and effort into System.Text.Json so we are going to get excellent support.
But before replacement, we need to ensure that migration to the new serializer won’t affect exists functionality and doesn’t introduce any breaking changes for existing logic.

4 Likes

I found more details here https://michaelscodingspot.com/the-battle-of-c-to-json-serializers-in-net-core-3/

The only surprise here is how poorly Newtonsoft.Json performed. This is probably due to the UTF-16 and UTF-8 issue. The HTTP protocol works with UTF-8 text. Newtonsoft converts this text into .NET string types, which are UTF-16. This overhead is not done in either Utf8Json or System.Text.Json , which work directly with UTF-8.

Please, add source code of benchmark

Do we have a possibility to replace nestonsoft.json by system.text.json in the autorest clients?

And what about non ASP.NET Core projects? Is they have some replacement newtonsoft.json?

This work is already done in the V3 generator.

Thank you. But it’s not useful for us, till we migrate to .NET Core version, right?

@pushnitsa you can install System.Text.Json NuGet package https://www.nuget.org/packages/System.Text.Json for .NET Framework 4.6 and later versions.

1 Like

Seems to be it is still require .net core :frowning:

Seems it is a well-known issue High disk IO volume after upgrading to 3.0 · Issue #17843 · dotnet/aspnetcore · GitHub

The Newtonsoft.Json based output-formatter buffers content. The buffering is done to avoid synchronous writes to the HTTP response since the serializer is entirely synchronous.

At any given time, the buffer keeps 32k in memory, followed by writing further content to disk. Consequently, your disk I/affects the response time for payloads larger than 32k. We’ve had a similar discussion about this: #17843. #17843 (comment) has some guidance on what you could consider doing in your application.

1 Like