DEV Community

Cover image for Stop using Newtonsoft.Json
Serhii Korol
Serhii Korol

Posted on

Stop using Newtonsoft.Json

Hello folks. In this article, I would like to illustrate why you no longer need to use the Newtonsoft.Json package to work with JSON.
Why don't I advise using Newtonsoft? The last release was on 8th March 2023. This project isn't developing and optimizing.
Now, there are three approaches to serializing and deserializing JSON.
I'll describe these approaches with code examples, make benchmarks, and show the performance difference. I'll also be using the latest version of .NET9.

Preconditions

It would help to have a base knowledge of C# and .NET. You should also install the BenchmarkDotNet and Newtonsoft.Json packages. And, of course, the .NET9 should also be installed.

Building base project

First, create a simple console application from the template and install the abovementioned packages.
Let's code. In a Program.cs file, add this code:

internal static class Program
{
    private static void Main()
    {
        BenchmarkRunner.Run<JsonPerformance>(new BenchmarkConfig());
    }
}
Enter fullscreen mode Exit fullscreen mode

This needed to run the benchmark. Further, let's add configuration for the benchmark.

public class BenchmarkConfig : ManualConfig
{
    public BenchmarkConfig()
    {
        AddJob(Job.ShortRun
            .WithRuntime(CoreRuntime.Core90)
            .WithJit(Jit.Default)
            .WithPlatform(Platform.X64)
        );

        AddLogger(ConsoleLogger.Default);
        AddColumnProvider(BenchmarkDotNet.Columns.DefaultColumnProviders.Instance);
        AddExporter(BenchmarkDotNet.Exporters.HtmlExporter.Default);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this code, I have added the runtime, JIT, and platform. If, for some reason, you are unable to install .NET9, you can use. .NET8. Let's look at the methods below. AddLogger is needed for the logging to the console. It is needed if you want to see the results of the performance tests in the console. AddColumnProvider is needed to generate the results in the table. AddExporter needs to export to the format you are interested in. I have chosen the HTML format. You can choose, for example, the Markdown format.
Next, let's create a simple model that we serialize to JSON.

public record Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public double YearsExperience { get; set; }
    public List<string> Languages { get; set; }
    public bool OpenToWork { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Next step, we need to seed data.

public static class Generator
{
    public static List<Person> GeneratePersons(int count)
    {
        var persons = new List<Person>();
        for (int i = 0; i < count; i++)
        {
            persons.Add(GeneratePerson());
        }
        return persons;
    }

    private static Person GeneratePerson()
    {
        var random = new Random();
        var randomNumber = random.Next(1, 100);
        var person = new Person
        {
            FirstName = Guid.NewGuid().ToString(),
            LastName = Guid.NewGuid().ToString(),
            Age = randomNumber,
            Languages = ["English", "French", "Spanish"],
            YearsExperience = random.NextDouble(),
            OpenToWork = randomNumber % 2 == 0
        };
        return person;
    }
}
Enter fullscreen mode Exit fullscreen mode

Building write and read JSON methods

Before that, let's create a new class, such as JsonPerformance. It will contain our methods. Into this class, put these properties that will keep serialized and deserialized data:

private List<Person> Persons { get; } = Generator.GeneratePersons(100000);
private string? Json { get; set; }
Enter fullscreen mode Exit fullscreen mode

Let's start with the realization of Newtonsoft's methods. It's typical operations with which you are likely familiar.

    [Benchmark]
    public void NewtonJsonWrite()
    {
        Json = JsonConvert.SerializeObject(Persons);
    }

    [Benchmark]
    public void NewtonJsonRead()
    {
        if (Json == null) return;
        var persons = JsonConvert.DeserializeObject<List<Person>>(Json);
    }
Enter fullscreen mode Exit fullscreen mode

In the next step, let's add methods using tools built into the framework. This realization is similar to Newtonsoft's.

    [Benchmark]
    public void FrameworkJsonWrite()
    {
        Json = JsonSerializer.Serialize(Persons);
    }

    [Benchmark]
    public void FrameworkJsonRead()
    {
        if (Json == null) return;
        var persons = JsonSerializer.Deserialize<List<Person>>(Json);
    }
Enter fullscreen mode Exit fullscreen mode

Finally, the last method is implemented with the manual approach, where you need to read and write data manually.

    [Benchmark]
    public void ManualJsonWrite()
    {
        var builder = new StringBuilder();
        var writer = new StringWriter(builder);
        using (var textWriter = new JsonTextWriter(writer))
        {
            textWriter.WriteStartArray();
            foreach (var person in Persons)
            {
                textWriter.WriteStartObject();

                textWriter.WritePropertyName("FirstName");
                textWriter.WriteValue(person.FirstName);

                textWriter.WritePropertyName("LastName");
                textWriter.WriteValue(person.LastName);

                textWriter.WritePropertyName("Age");
                textWriter.WriteValue(person.Age);

                textWriter.WritePropertyName("YearsExperience");
                textWriter.WriteValue(person.YearsExperience);

                textWriter.WritePropertyName("Languages");
                textWriter.WriteStartArray();
                foreach (var language in person.Languages)
                {
                    textWriter.WriteValue(language);
                }
                textWriter.WriteEndArray();

                textWriter.WritePropertyName("OpenToWork");
                textWriter.WriteValue(person.OpenToWork);

                textWriter.WriteEndObject();
            }
            textWriter.WriteEndArray();
        }
        Json = builder.ToString();
    }

    [Benchmark]
    public void ManualJsonRead()
    {
        if (Json == null) return;
        var persons = new List<Person>();
        using var reader = new StringReader(Json);
        using var jsonReader = new JsonTextReader(reader);
        jsonReader.Read();
        while (jsonReader.Read())
        {
            if (jsonReader.TokenType == JsonToken.StartObject)
            {
                var person = new Person();
                while (jsonReader.Read())
                {
                    if (jsonReader.TokenType == JsonToken.PropertyName)
                    {
                        var propertyName = jsonReader.Value?.ToString();
                        jsonReader.Read();
                        switch (propertyName)
                        {
                            case "FirstName":
                                person.FirstName = jsonReader.Value?.ToString() ?? string.Empty;
                                break;
                            case "LastName":
                                person.LastName = jsonReader.Value?.ToString() ?? string.Empty;
                                break;
                            case "Age":
                                person.Age = Convert.ToInt32(jsonReader.Value);
                                break;
                            case "YearsExperience":
                                person.YearsExperience = Convert.ToInt32(jsonReader.Value);
                                break;
                            case "Languages":
                                person.Languages = jsonReader.Value?.ToString()?.Split(',').ToList() ?? new List<string>();
                                break;
                            case "OpenToWork":
                                person.OpenToWork = Convert.ToBoolean(jsonReader.Value);
                                break;
                        }
                    }
                    else if (jsonReader.TokenType == JsonToken.EndObject)
                    {
                        break;
                    }
                }
                persons.Add(person);
            }
            else if (jsonReader.TokenType == JsonToken.EndArray)
            {
                break;
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

Performance

When we implement all methods, we can check performance. I'll take a series of benchmarks with large quantities of items, starting from 100000 items. Check this out. Before we start, you should switch your project to Release mode. Let's go.

As you can see, Newtonsoft performs lower than other methods. It is lowest in writing and faster in reading. The manual approach performs similarly to the system approach in writing but is lower in reading.

100000

Conclusions

I recommend using a system tool to work with JSON. It is faster than other approaches and is a part of the framework that constantly develops and optimizes for the latest version of .NET.

Thanks a lot for reading, and happy coding.

The source code is HERE.

Buy Me A Beer

Top comments (5)

Collapse
 
florianrappl profile image
Florian Rappl

"I recommend using a system tool" - hm? A system tool? You rather mean "a class from the system namespace"?

Sure you should use the System.Text.Json package these days, but there are some good reasons to still stick with Newtonsoft.Json. One of the reasons is that System still has no support for JSON Schema, which is super important for many scenarios.

Collapse
 
serhii_korol_ab7776c50dba profile image
Serhii Korol

You can use the "JsonSchema.Net" package that it uses "System.Text.Json." Anyway, it'll be faster than Newtonsoft.Json, which has not been supported for 1.5 years.

Collapse
 
panditapan profile image
Pandita

"Why don't I advise using Newtonsoft? The last release was on 8th March 2023. This project isn't developing and optimizing."

You have now created a little bug inside my brain... when is a software project done? You did a performance test where the differences are in nanoseconds, does a software project really need to continue optimizing until it's in another realm of performance? would that be enough to consider it done? 🤔

Regardless of that, I think this is valid consideration if you're creating a project where your potential clients have horrible internet but if you're aiming for normal internet users I'm not sure if it matters too much! I could be wrong though. Anyway, great post!! :3

Collapse
 
strredwolf profile image
STrRedWolf

Faster... but is it easier?

I think that's what NewtonSoft.Json is doing: instead of having to constantly write/rewrite plumbing to get your object de/serialized, it's giving you a method to convert it.

In other words, it's not entirely apples to apples, isn't it? What methods are there for doing a drop-in, generic object de/serialization?

Collapse
 
hectorlaris profile image
Héctor Serrano

Today I knew about JsonPerformance class, thankyou Sergii!