With the release of .NET 6 also came the option to use source generation in System.Text.Json. In short this means that instead of runtime reflection some of the work is now done compile time. This makes it more efficient. But how much? And should I care?
Just measure it. I made a small project that you can use and adjust to measure the difference for your specific case. Let the results help you decide what's best for your application.
The code can be found here: netwatwezoeken/jsonbenchmark (github.com).
It uses BenchmarkDotNet for measurements and Bogus to generate fake data. Both serialization and de-serialization are measured. I chose to have the following Book as object under test. Both a list of one thousand books and a and single Book are benchmarked.
I included Newtonsoft for reference. Hence the project contains three json (de)serialization methods
- Generated System.Text.Json
Settings for method 1 and 2 can be found in
SerializationOptions.cs. Settings for generated System.Text.Json are in
To verify that all serialization settings are compliant for all three mechanisms a couple of test are available. These allow you to validate if all methods give the same result. We are trying to compare apples to apples.
To get results run the App in Release mode. To modify the benchmarks just replace
Book by your own class or record.
Simple as that! Continue reading to see some of my results and findings.
As can be seen in the results below deserialization is 7% (
Book) and 10% (
List<Book) slower with the source generator compared to normal System.Text.Json.
However the performance gain when serializing
List<Book> seems higher (27%) compared to serializing a single
Memory wise the differences are small.
Benchmarks were executed on:
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000 AMD Ryzen 9 5900X, 1 CPU, 24 logical and 12 physical cores .NET SDK=6.0.100
Specifics worth mentioning
Most of the code should be self explanatory. Hopefully it will help you to do some measurements yourself and come to a good decision.
There are a few things I'd like to mention:
- Generated serialization it needs a
JsonConverterAttribute on enum
JsonStringEnumConverterto work. I was not able to set this in
- Some author names were represented in escaped unicode by
System.Text.Jsonbut not by Newtonsoft. Thefore
UnsafeRelaxedJsonEscapingis added to the settings for method 2 and
Unescape()is used in the unittest of method 3. Thank Bogus for providing good data!
- Enums in nested classes result in a compiler error. As a workaround, explicitly add any nested enum to
JsonSerializableattribute. See also https://github.com/dotnet/runtime/issues/61860.
Things to consider
- How will it affect your application? Only expect significant results when your application is doing a lot of (de)serialization. In a few cases processing time might matter. I would not bother optimizing something that is only executed once a minute and already performs well within limits.
- When moving from one method to the other, be sure to thoroughly test your (de)serialization results and double check the settings. As an example: Newtonsoft is case insensitive by default. System.Text.Json is not.
- Personally, I would not mix Newtonsoft and System.Text.Json in a single solution. However, mixing generated and reflection based System.Text.Json serialization could be interesting. Only apply source generation where valuable.
- Additional code. You will add code that needs maintenance. Not much, but you do need to list which classes you plan to (de)serialize. Also take care that the serialization settings are now in a different place. Mixed use of (de)serialization might require additional caution to keep the settings in sync.
- The difference between Newtonsoft and System.Text.Json might not be that dramatic for your application: What Those Benchmarks Of System.Text.Json Don't Mention (dotnetcoretutorials.com)
By Jos Hendriks
Like the article or want to give some feedback? Feel free to reach out through any of my socials« Back to home