Try the new System.Text.Json APIs

Avatar

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:

ScenarioSpeedMemory
Deserialization2x fasterParity or lower
Serialization1.5x fasterParity or lower
Document (read-only)3-5x faster~Allocation free for sizes < 1 MB
Reader2-3x faster~Allocation free (until you materialize values)
Writer1.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)

DescriptionRPSCPU (%)Memory (MB)
Newtonsoft.Json – 500 B136,43595172
System.Text.Json – 500 B167,86194169
Newtonsoft.Json – 2.4 KB97,13797174
System.Text.Json – 2.4 KB132,02696169
Newtonsoft.Json – 40 KB7,71288212
System.Text.Json – 40 KB16,62596193

 

JSON serialization (output)

DescriptionRPSCPU (%)Memory (MB)
Newtonsoft.Json – 500 B120,27394174
System.Text.Json – 500 B145,63194173
Newtonsoft.Json – 8 KB35,40898187
System.Text.Json – 8 KB56,42497184
Newtonsoft.Json – 40 KB8,41699202
System.Text.Json – 40 KB14,84898197

 

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!"}

Avatar
Immo Landwerth

Program Manager, .NET

Follow Immo   

44 Comments
Drew Miller
Drew Miller 2019-06-13 15:41:16
Yes! Just what I needed for an upcoming edge device. Thank you.
Avatar
Eric Newton 2019-06-13 16:35:27
Ok, so just like always, you built your own, which will be substandard, incompatible, and slower for real world cases.  I bet your contrived examples were faster... publish all the real world cases that came in slower... in the interest of full disclosure. And to top it off, you wrote it into System.Text.Json namespace, so now we'll usually have two JSON libraries deployed: JSON.net and yours. Brilliance!  Classic Microsoft "Not Invented Here" syndrome that plagues the entire MS infrastructure.  Note how the worst security bugs are mostly Microsoft's?  There's an underlying symptom for that... and its because you keep copying other people's stuff and trying to write it better, while not understanding the reasons for why that stuff exists in the first place.
Avatar
Greg Ingram 2019-06-14 03:01:00
Is the plan to create JSON schema APIs, as well?  Good stuff and great work team!
Avatar
PeteA 2019-06-14 08:20:25
The Json.NET versioning situation is a serious pain-point atm, so it's good to see an attempt to solve it.  Can't help but think about standards though (and yes, I did note that Json.NET isn't being discarded, but it's needed for "some use case" then I foresee having to try to keep the settings for two different libraries consistent within one codebase - ugh).  Also, what's the story on camelCase?  As this is brand-new library that doesn't have to maintain backward compatibility, any chance of taking the plunge and defaulting to idiomatic casing on both sides?
Daniel Morris
Daniel Morris 2019-06-14 08:39:02
Honestly, it seems like a solution in search of a problem.  We've been using the newtonsoft product for years and it's one of the few modules that has never caused us problems.  But, whatever. I just hope it doesn't cause conflicts. 
Vovchik Ivanov
Vovchik Ivanov 2019-06-14 09:03:56
Very nice. Thank you for the article. Read DOM access is nice but what about write access? Are there any plans for ability to modify DOM?
Avatar
Remy Reynold de Seresin 2019-06-14 11:45:50
What is the performance improvement on techempower JSON serialization benchmarks ?
Mark Adamson
Mark Adamson 2019-06-14 14:13:50
I just tried converting a side project to use this but unfortunately it doesn't support parameters in constructors and therefore doesn't support immutable objects, which is a shame. I'd come to accept that with the old XML serializers but I've been using it happily in Json.NET for a while. I've added a comment to https://github.com/dotnet/corefx/issues/38163
Avatar
Robin Sue 2019-06-14 14:51:36
So why is the new API using Parse and ToString instead of Serialize and Deserialize? Every prior serializer i've seen in the BCL and in the wild also used these terms and you can't get around describing those features using Serialize(ation) and Deserialize.
Avatar
Thad House 2019-06-14 17:32:58
Is there any known issues with round tripping using JsonSerializer.WriteAsync and JsonSerializer.ReadAsync between 2 threads/TCP clients in the same process? I'm trying to test it out with a very simple test app, and have verified that the JSON is getting written to the socket, but the ReadAsync never returns.
Cosmin Popescu
Cosmin Popescu 2019-06-14 21:36:24
Sometimes it's good to have native support for json. Like php, for exemple.
Avatar
Şafak Gür 2019-06-15 05:27:33
Seems nice, a couple questions: * Will data contract attributes work like in Json.NET? * Does it support deserialization using the constructor (for immutable objects)? * Why GetString/GetInt32? Is there a generic Get overload that also supports custom/complex types? * Can we write to DOM? I love LINQ to XML - it's my favorite way to build an XML document via code, so the ability to create JSON the same way would make me very happy 
Avatar
Jeff Johnson 2019-06-15 07:29:43
Thanks for sharing. The less nuget packages the better. For those who don't want it, they can stick with newtonsoft json and ignore this namespace entirely, no harm done. For me I will be switching to it and reducing my nuget package count by 1 :)
Avatar
. . 2019-06-15 08:43:01
Is it possible to allow single quote around property name or value? { 'prop': 'value' }I get the following error. System.Text.Json.JsonException: ''' is an invalid start of a property name. Expected a '"'. Path: $ | LineNumber: 0 |
Avatar
Shmueli Englard 2019-06-16 13:09:11
I'm curious about how easy is would be to make code that can be serialized by either? Like I don't care what serializer is used, but I want to control the name of the json properties?
Avatar
何 于 2019-06-17 00:42:52
Is there merge method? So I can use the object pool.
Avatar
Tony Henrique 2019-06-18 09:47:39
Some very good speed improvements! Congratulations.This will make the Azure Functions even snappier.
Avatar
Asbjørn Riis-Knudsen 2019-06-18 13:37:27
Every time you show System.Text.Json you take the most simple example possible. No need to have different name in JSON compared to C#, no need to serialize enums as strings. How does System.Text.Json handle this? Also, is camelCase finally the default?
Avatar
anonymous 2019-06-20 01:26:06
This comment has been deleted.
Avatar
Radek Kolář 2019-06-20 02:28:45
I understand that the System.Text.Json is still in development. However, I would like to point out that the new deserializer produces very different results than the previous one. Details - https://github.com/dotnet/corefx/issues/38713
Avatar
路 黄 2019-06-20 20:51:29
JsonDemo jsonObj = new JsonDemo() { Age = 12, Name = "方法" };string jsonStr = System.Text.Json.Serialization.JsonSerializer.ToString(jsonObj); //the jsonStr = {"Name":"\u65b9\u6cd5","Age":12}   
Avatar
Wouter Kettlitz 2019-06-24 02:29:45
How do you solve self-referencing problems?  .AddNewtonsoftJson(options =>{options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;});
Avatar
SeHyun Nam(남세현) 2019-06-24 20:33:10
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.
Avatar
Rallabhandi, Madhukar 2019-06-26 00:39:55
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!
Avatar
Andriy Savin 2019-07-16 10:49:46
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?