Try the new System.Text.Json APIs
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-16string
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. WithSystem.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:
- Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package.
- In
ConfigureServices()
add a call toAddNewtonsoftJson()
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.
- Install the
Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson
NuGet package. - On the client add
.AddNewtonsoftJsonProtocol()
to theHubConnectionBuilder
: - On the server add
.AddNewtonsoftJsonProtocol()
to theAddSignalR()
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
I currently try to evaluate whether we can switch from “Newtownsoft” to “System.Text.Json” in future. One missing functionality I found is missing supports for serializing enums to their text name (without need to write converter). Is this feature planed in some of the next releases? Thx upfront
That is supported similar to how Newtonsoft.Json does it. Use the JsonStringEnumConverter: https://docs.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonstringenumconverter?view=netcore-3.1
Here’s an example in the docs on how to do that: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?view=netcore-3.1#enums-as-strings
It would have been nice if it worked 🙂
I have an object with a nested object, with the nested object containing zero references to the containing object, and still, when I try to return this from an api controller, .net goes bananas, allocates 4G of memory and crashes. If I do a serialize from Newtonsoft, it works like a charm, no problems.
I am not quite sure why this happens yet, but when I find out I’ll post some example code. I know that it is the nested object that causes the problem, since setting it to null prevents (what I assume is the circular reference from) gobbling up memory. I can only assume that we are talking about a reference from the child object to the parent object causing this, but as defined the child class contains no references to the parent class, so I have to assume it is some sort of internal references being followed.
OK, so here’s the code that will crash the new JSON Formatter, basically rendering it completely useless at this point in time, tested with .net core 3.0 preview 8.
public class AUser{ public string Fullname { get; set; } public AFolder HomeFolder { get; set; }}
public class AFolder{ public string Name { get; set; } public IList<AFolder> SubFolders { get; set; }}
In some code somewhere do
var user = new AUser { HomeFolder = new AFolder { SubFolders = null // For good measure }}
var t = System.Text.Json.JsonSerializer.Serialize(user);
I am unable to repro the issue you are seeing with the GA release of .NET Core 3.0 and 3.1 (LTS). Looks like it was a bug that was fixed. The sample code you shared outputs: {“Fullname”:null,”HomeFolder”:{“Name”:null,”SubFolders”:null}}
It does not support Dictionary. Had to fallback to Newtonsoft.Json
What types of dictionaries do you have? With System.Text.Json, the keys must be string. Maybe this doc will help: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?view=netcore-3.1#support-dictionary-with-non-string-key
Is there a way to get the class type in JSON on serializing. This is required to identify the sub classes.
This seems like a step backwards. Traversing and looping though the JSON like its XML doesn’t appeal to me. I greatly prefer the Newtonsoft method.
Hat tip for trying something new – but this will need a few iterations before I would consider putting it in my toolbox.
It’s terrible.I have to use JSON.NET in my project.But AddNewtonsoftJson have a problems.
https://github.com/dotnet/corefx/issues/41886
Can’t find a way to set up the serializer options using dependency injection.
Why not just improve on the existing System.Runtime.Serialization.Json.JsonReaderWriterFactory?
I did performance tests among all Json providers i use and i found that fastJson is the fastest among all.
https://github.com/mgholam/fastJSON
See following question:
https://stackoverflow.com/questions/58225122/serialize-deserialize-a-class-hierarchy-with-net-core-system-text-json
And one side comment:
Seems System.Text.Json has quite low level API. I switched to Newtonsoft JSON but will continue looking at how System.Text.Json evolves. – shadeglare Oct 31 at 13:01
Can you please tackle with this problem ?
Maybe it’s sorted out already, then just leave answer to stack overflow, maybe ?