.NET Performance Analysis: Newtonsoft.Json vs System.Text.Json in .NET 8

Trevor McCubbin
5 min readJan 28, 2024
Photo by Pankaj Patel on Unsplash

Introduction

As a passionate .NET developer, I find myself continuously seeking opportunities to expand my understanding of the .NET ecosystem. In this, my first article ever, I embark on a journey to explore the intricacies of .NET performance, with a specific focus on two prominent JSON frameworks: Newtonsoft.Json and Microsoft’s System.Text.Json. My goal is to share the insights and revelations gained during this exploration with fellow developers who, like me, are keen on deciphering the nuances within our tech ecosystem.

This article is inspired by Tobias Streng’s noteworthy article on .NET 7 performance and I’ve tailored my investigation to align with the latest advancements in .NET 8. Join me on this quest as we uncover the nuances between these two JSON powerhouses and gain a deeper understanding of their performance implications in real-world scenarios.

Original Article: .NET Performance #2: Newtonsoft vs. System.Text.Json by Tobias Streng

Framework Popularity

As of January 27th, 2024, Newtonsoft.Json boasts an impressive record of over 4.2 billion downloads, securing its position as the most downloaded package on NuGet. In contrast, System.Text.Json lags behind with approximately 1.8 billion downloads. Notably, System.Text.Json’s inclusion as a default in the .NET SDK since .NET Core 3.1 significantly contributes to its widespread adoption.

Comparing these numbers to the original .NET 7 article reveals a compelling narrative. At that time, Newtonsoft.Json had accumulated 2.3 billion downloads, signifying an 82.6% increase in download count over the 15-month period. In the same timeframe, System.Text.Json experienced a remarkable growth of 200%, suggesting a faster pace of adoption. However, when examining the sheer download numbers, Newtonsoft.Json added a staggering 1.9 billion downloads in this period — surpassing the total downloads for System.Text.Json since its introduction into the .NET SDK in 2019.

Benchmark Scenarios

To recreate the same scenarios as the original article, we’ll focus on two main use cases:

  1. Serialization and deserialization of a single large dataset.
  2. Serialization and deserialization of many small datasets.

For test data, we will leverage the NuGet package Bogus to generate random users with their own unique identity.

[Params(10000)]
public int Count { get; set; }

private List<User> testUsers = [ ];

[GlobalSetup]
public void GlobalSetup()
{
var faker = new Faker<User>().CustomInstantiator(
f =>
new User(
Guid.NewGuid(),
f.Name.FirstName(),
f.Name.LastName(),
f.Name.FullName(),
f.Internet.UserName(f.Name.FirstName(), f.Name.LastName()),
f.Internet.Email(f.Name.FirstName(), f.Name.LastName())
)
);

testUsers = faker.Generate(Count);
}

Benchmark Setup

  • Newtonsoft.Json 13.0.3
  • System.Text.Json 8.0.1
  • Bogus 35.4.0
  • BenchmarkDotNet 0.13.12

Serialization Benchmarks

Serialize Big Data Object

In this benchmark, we examine the serialization performance of a single large object using the List<User> data structure. Both frameworks utilize the default ContractResolver.

[Benchmark]
public void NewtonsoftSerializeBigData() =>
_ = Newtonsoft.Json.JsonConvert.SerializeObject(testUsers);

[Benchmark]
public void MicrosoftSerializeBigData() =>
_ = System.Text.Json.JsonSerializer.Serialize(testUsers);

Results:

The results mirror those found in the .NET 7 analysis, where System.Text.Json outperforms Newtonsoft.Json by over twice the speed. Microsoft’s package also exhibits superior memory efficiency, using less than half the memory compared to Newtonsoft.Json.

Serialize Big Data Object With Custom Json Serializor Settings

In this scenario, we revisit the previous serialization test, introducing a new element: converting the JSON properties to snake case. Note that:

instantiating the ContractResolver more than once can incur a performance hit, so careful consideration is needed.

[Benchmark]
public void NewtonsoftSerializeBigDataWithSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
}
};

_ = Newtonsoft.Json.JsonConvert.SerializeObject(testUsers, settings);
}

[Benchmark]
public void MicrosoftSerializeBigDataWithSettings()
{
var settings = new JsonSerializerOptions()
{
WriteIndented = true,
PropertyNamingPolicy = new SnakeCasePropertyNamingPolicy()
};

_ = System.Text.Json.JsonSerializer.Serialize(testUsers, settings);
}

Results:

The results indicate that both Newtonsoft.Json and Microsoft’s System.Text.Json experience a performance hit and increased memory usage when applying a custom naming policy. However, Newtonsoft.Json shows a more significant increase in mean execution time (4.678 ms) compared to System.Text.Json (1.493 ms), suggesting that Newtonsoft.Json takes approximately 213% more time to execute than Microsoft’s package when applying the same naming policy.

Serialize Many Small Data Objects

This scenario represents a realistic use-case for JSON serialization, closely simulating REST APIs. The benchmark involves looping through the List<User> and serializing each user individually.

[Benchmark]
public void NewtonsoftSerializeIndividualData()
{
foreach (var user in testUsers)
{
_ = Newtonsoft.Json.JsonConvert.SerializeObject(user);
}
}

[Benchmark]
public void MicrosoftSerializeIndividualData()
{
foreach (var user in testUsers)
{
_ = System.Text.Json.JsonSerializer.Serialize(user);
}
}

Results:

As observed, Microsoft’s System.Text.Json once again demonstrates faster mean execution time compared to Newtonsoft.Json. Additionally, it is important to note the significant difference in memory allocation between the two packages. Tobias Streng emphasizes the importance of saving heap memory, considering its impact on overall application performance.

“saving heap memory is even more important than the speed, you are seeing here. Heap memory will eventually have to be garbage collected, which will block your entire application from executing”

Deserialization Benchmarks

Deserialize Big Data Object

Now, we’ll shift our focus to deserialization, starting with a benchmark for deserializing one large JSON string into the respective .NET object List<User>.

[Benchmark]
public void NewtonsoftDeserializeBigData() =>
_ = Newtonsoft.Json.JsonConvert.DeserializeObject<List<User>>(serializedTestUsers);

[Benchmark]
public void MicrosoftDeserializeBigData() =>
_ = System.Text.Json.JsonSerializer.Deserialize<List<User>>(serializedTestUsers);

Results:

Notably, comparing Newtonsoft to Microsoft in deserialization, there haven’t been substantial changes over the past year. While Microsoft appears to have made small optimizations in memory allocation, the overall trend indicates that Microsoft is much faster than Newtonsoft.

Deserialize Many Small Data Objects

Finally, we’ll benchmark the deserialization of many small objects from a List<string>.

[Benchmark]
public void NewtonsoftDeserializeIndividualData()
{
foreach (var user in serializedTestUsersList)
{
_ = Newtonsoft.Json.JsonConvert.DeserializeObject<User>(user);
}
}

[Benchmark]
public void MicrosoftDeserializeIndividualData()
{
foreach (var user in serializedTestUsersList)
{
_ = System.Text.Json.JsonSerializer.Deserialize<User>(user);
}
}

Results:

Once again, Microsoft demonstrates nearly twice the speed and astonishingly requires over 30 MB less than Newtonsoft for deserialization. This echoes the findings of the .NET 7 benchmark, indicating a consistent performance advantage for Microsoft. Additionally, it seems that Microsoft has made further optimizations, using slightly less memory than last year.

Conclusion

In the realm of JSON serialization and deserialization within the .NET 8 landscape, our benchmarks present a compelling case. Despite claims of high performance from Newtonsoft.Json, the results unequivocally demonstrate that Microsoft’s System.Text.Json consistently outperforms its counterpart. Whether handling large or small datasets, System.Text.Json showcases superior speed and memory efficiency.

Key Takeaways:

  • Serialization and Deserialization Performance: System.Text.Json consistently excels in both serializing and deserializing tasks.
  • Memory Efficiency: The SDK-native package, System.Text.Json, not only outpaces Newtonsoft.Json in speed but also demonstrates remarkable efficiency in memory allocation.

These findings are specific to .NET 8, and it’s important to recognize that performance characteristics may vary with different versions. Building upon the insights gained from .NET 7, it’s reasonable to assert that System.Text.Json stands as the faster choice in all tested scenarios for .NET 7 and 8. While assumptions about future versions remain uncertain, the recent alignment of the creator of Newtonsoft.Json with Microsoft suggests a potential trajectory favoring System.Text.Json.

You can explore all the benchmark code used in this article on my GitHub repository:

Thank you for engaging in this exploration of .NET performance. Your thoughts and feedback are welcome. If you’re intrigued by further adventures in the .NET ecosystem or keen on unraveling the nuances of performance, consider following me.

Original Article: .NET Performance #2: Newtonsoft vs. System.Text.Json by Tobias Streng

--

--