Try the new System.Text.Json APIs

Immo

For .NET Core 3.0, we’re shipping a brand new namespace called System.Text.Json with support for a reader/writer, a document object model (DOM), and a serializer. In this blog post, I’m telling you why we built it, how it works, and how you can try it.

We also have a video:

Getting the new JSON library

  • If you’re targeting .NET Core. Install the latest version of the .NET Core 3.0 preview. This gives you the new JSON library and the ASP.NET Core integration.
  • If you’re targeting .NET Standard or .NET Framework. Install the System.Text.Json NuGet package (make sure to include previews and install version 4.6.0-preview6.19303.8 or higher). In order to get the integration with ASP.NET Core, you must target .NET Core 3.0.

The future of JSON in .NET Core 3.0

JSON has become an essential part of virtually all modern .NET applications and in many cases even surpassed the usage of XML. However, .NET hasn’t had a (great) built-in way to deal with JSON. Instead, we’ve relied on Json.NET until now, which continues to serve the .NET ecosystem well.

We’ve decided that we needed to build a new JSON library:

  • Provide high-performance JSON APIs. We needed a new set of JSON APIs that are highly tuned for performance by using Span<T> and can process UTF-8 directly without having to transcode to UTF-16 string instances. Both aspects are critical for ASP.NET Core, where throughput is a key requirement. We considered contributing changes to Json.NET, but this was deemed close to impossible without either breaking existing Json.NET customers or compromising on the performance we could achieve. With System.Text.Json, we were able to gain 1.3x – 5x speed up, depending on the scenario (see below for more details). And we believe we can still squeeze out more.
  • Remove Json.NET dependency from ASP.NET Core. Today, ASP.NET Core has a dependency on Json.NET. While this provides a tight integration between ASP.NET Core and Json.NET, it also means the version of Json.NET is dictated by the underlying platform. However, Json.NET is frequently updated and application developers often want to — or even have to — use a specific version. Thus, we want to remove the Json.NET dependency from ASP.NET Core 3.0, so that customers can choose which version to use, without fearing they might accidentally break the underlying platform.
  • Provide an ASP.NET Core integration package for Json.NET. Json.NET has basically become the Swiss Army knife of JSON processing in .NET. It provides many options and facilities that allow customers to handle their JSON needs with ease. We don’t want to compromise on the Json.NET support customers are getting today. For example, the ability to configure the JSON serialization in ASP.NET Core via the AddJsonOptions extension method. Thus, we want to provide the Json.NET integration for ASP.NET Core as a NuGet package that developers can optionally install, so they get all the bells and whistles they get from Json.NET today. The other part of this work item is to ensure we have the right extension points so that other parties can provide similar integration packages for their JSON library of choice.

For more details on the motivation and how it relates to Json.NET, take a look at the announcement we made back in October.

Using System.Text.Json directly

For all the samples, make sure you import the following two namespaces:

Using the serializer

The System.Text.Json serializer can read and write JSON asynchronously and is optimized for UTF-8 text, making it ideal for REST API and back-end applications.

By default, we produce minified JSON. If you want to produce something that is human readable, you can pass in an instance of JsonSerializerOptions to the serializer. This is also the way you configure other settings, such as handling of comments, trailing commas, and naming policies.

Deserialization works similarly:

We also support asynchronous serialization and deserialization:

You can also use custom attributes to control serialization behavior, for example, ignoring properties and specifying the name of the property in the JSON:

We currently don’t have support for F# specific behaviors (such as discriminated unions and record types), but we plan on adding this in the future.

Using the DOM

Sometimes you don’t want to deserialize a JSON payload, but you still want structured access to its contents. For example, let’s say we have a collection of temperatures and want to average out the temperatures on Mondays:

The JsonDocument class allows you to access the individual properties and values quite easily.

Using the writer

The writer is straight forward to use:

The reader requires you to switch on the token type:

Integration with ASP.NET Core

Most use of JSON inside of ASP.NET Core is provided via the automatic serialization when accepting or returning object payloads, which in turn means that most of your application’s code is agnostic to which JSON library ASP.NET Core is using. That makes it easy to switch from one to another.

You can see the details on how you can enable the new JSON library in MVC and SignalR later on in this post.

Integration with ASP.NET Core MVC

In Preview 5, ASP.NET Core MVC added support for reading and writing JSON using System.Text.Json. Starting with Preview 6, the new JSON library is used by default for serializing and deserializing JSON payloads.

Options for the serializer can be configured using MvcOptions:

If you’d like to switch back to the previous default of using Newtonsoft.Json, do the following:

  1. Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
  2. In ConfigureServices() add a call to AddNewtonsoftJson()

Known issues

  • Support for OpenAPI / Swagger when using System.Text.Json is ongoing and unlikely to be available as part of the 3.0 release.

Integration with SignalR

System.Text.Json is now the default Hub Protocol used by SignalR clients and servers starting in ASP.NET Core 3.0 Preview 5.

If you’d like to switch back to the previous default of using Newtonsoft.Json, then you can do so on both the client and server.

  1. Install the Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson NuGet package.
  2. On the client add .AddNewtonsoftJsonProtocol() to the HubConnectionBuilder:
  3. On the server add .AddNewtonsoftJsonProtocol() to the AddSignalR() call:

Performance

Since this feature is heavily motivated by performance, we’d like to share some high-level performance characteristics of the new APIs.

Please keep in mind that these are based on preview builds and the final numbers will most likely differ. We’re also still tweaking default behaviors which will affect performance (for example, case sensitivity). Please note that these are micro benchmarks. Your mileage will most certainly differ, so if performance is critical for you, make sure to make your own measurements for scenarios that best represent your workload. If you encounter scenarios you’d like us to optimize further, please file a bug.

Raw System.Text.Json

Just doing micro benchmarks to compare System.Text.Json with Json.NET yields the following output:

Scenario Speed Memory
Deserialization 2x faster Parity or lower
Serialization 1.5x faster Parity or lower
Document (read-only) 3-5x faster ~Allocation free for sizes < 1 MB
Reader 2-3x faster ~Allocation free (until you materialize values)
Writer 1.3-1.6x faster ~Allocation free

 

System.Text.Json in ASP.NET Core MVC

We’ve written an ASP.NET Core app that generates data on the fly that is then serialized and deserialized from MVC controllers. We then varied the payload sizes and measured the results:

JSON deserialization (input)

Description RPS CPU (%) Memory (MB)
Newtonsoft.Json – 500 B 136,435 95 172
System.Text.Json – 500 B 167,861 94 169
Newtonsoft.Json – 2.4 KB 97,137 97 174
System.Text.Json – 2.4 KB 132,026 96 169
Newtonsoft.Json – 40 KB 7,712 88 212
System.Text.Json – 40 KB 16,625 96 193

 

JSON serialization (output)

Description RPS CPU (%) Memory (MB)
Newtonsoft.Json – 500 B 120,273 94 174
System.Text.Json – 500 B 145,631 94 173
Newtonsoft.Json – 8 KB 35,408 98 187
System.Text.Json – 8 KB 56,424 97 184
Newtonsoft.Json – 40 KB 8,416 99 202
System.Text.Json – 40 KB 14,848 98 197

 

For the most common payload sizes, System.Text.Json offers about 20% throughput increase in MVC during input and output formatting with a smaller memory footprint.

Summary

In .NET Core 3.0, we’ll ship the new System.Text.Json APIs, which provide built-in support for JSON, including reader/writer, read-only DOM, and serializer/deserializer. The primary goal was performance and we see typical speedups of up to 2x over Json.NET, but it depends on your scenario and your payload, so make sure you measure what’s important to you.

ASP.NET Core 3.0 includes support for System.Text.Json, which is enabled by default.

Give System.Text.Json a try and send us feedback!

{"happy": "coding!"}

73 comments

Comments are closed. Login to edit/delete your existing comments

  • Wouter Kettlitz

    How do you solve self-referencing problems? 

    .AddNewtonsoftJson(options =>{options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;});

  • SeHyun Nam(남세현)

    I have a question about performance. As you know, some json library like Jil(https://github.com/kevin-montrose/Jil) or Utf8Json(https://github.com/neuecc/Utf8Json) use ILGenerator and make serializer for type dynamically to serialize that faster at next time.
    Is there any that kind of skills in System.Text.Json? Is it possitive to add that kind of skill? I have a mind to contribute for that but I just want to know the roadmap or skills which System.Text.Json will use.

  • Rallabhandi, Madhukar

    Hi,I have my applications on older .netcoreapp 2.1 framework, which has dependenices from my internal SDKs which inturn also point to the .netcoreapp 2.1 framework. And we are using Newtonsoft.Json extensively throughout our code. However now I want to test out the performance of this new System.text.Json library, so I ended up targeting all my projects  (and dependant projects) to new 3.0 preview builds and with some hassle,I am able to bring up my server.  However once I start hitting my Endpoints/Routes, I get lot of exceptions from various dlls.Exception thrown: ‘System.NotSupportedException’ in System.Text.Json.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllException thrown: ‘System.NotSupportedException’ in System.Private.CoreLib.dllAnd Newtonsoft objecst are not gettin recognized as well.  I want to avoid refactoring my entire codebase (which is huge) to accomodate new library. Is there a way for these two libraries to coexist and talk to each other? Any migration document from an older version to newer version will also help?
    Thanks!

  • Andriy Savin

    Hi Immo,
    Since this new implementation is all about performance, I’m wondering if it supports streamed document parsing without loading it all (or whole values if they are large) in memory? Let me give you a real example. I’m using a Web API (provided by a third party) which stores files and returns file content on request. There is a crazy detail here that the file content is returned as part of JSON document, encoded as a base64 JSON property value. So the proberty value can be of arbitrary size like megabytes or handreds of megabytes. Now, I need to read such value and write file content to some other storage. But to do that with, say, Json.NET I have to load whole value in memory, which is crazy for a server application. The alternative is to do some custom tricky parsing of the response stream. But instead I would be happy to “read” that property value as a stream (and the whole response body should not be fully loaded into memory). So I’m wondering if the new library can do this?

  • Alexander Paskhin

    Hi Immo, Just decided to use System.Text.Json instead of Newtonsoft.Json, but found that iSystem.Text.Json cannot parse the Decimal literals. So they are not compatible. Where is the best place to submit an issue?
     string test_json_string = “[{\”price_usd\”:9934.36214151},{\”price_usd\”:\”9934.36214151\”}]”;
    This test string works for “Newtonsoft.Json” but fails for “System.Text.Json”, but it should work as well.

  • Alexander Paskhin

    Just decided to use System.Text.Json instead of Newtonsoft.Json, but found that System.Text.Json cannot parse the Decimal literals. So they are not compatible.
      string test_json_string = “[{\”price_usd\”:9934.36214151},{\”price_usd\”:\”9934.36214151\”}]”;
    New System.Text.Json parser cannot parse the string above, but Newtonsoft.Json can

  • Noobish Noob

    Why is it only serializing properties ? 
    I have fields, and I don’t want properties…

  • Noobish Noob

    Serializing fields works with Newtonsoft.Json. It doesn’t work with System.Text.Json. The reason I use Newtonsoft.Json has always been that I can use fields, and don’t have to pollute the code with Json-attributes unless absolutely necessary. 

  • endofunk

    When do you expect to support fields with a constructor? …for example:

    public class Acme {
      public readonly int Id;
      public readonly string Name;
      …
      public Acme(int id, string name) { … }
    }

    Also can you provide examples of custom conversions e.g. int to enum, custom data types conversion, etc…