Try the new System.Text.Json APIs

Immo Landwerth

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

Discussion is closed. Login to edit/delete existing comments.

  • Drew Miller 0

    Yes! Just what I needed for an upcoming edge device. Thank you.

  • Eric Newton 0

    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.

    • Chris 1

      James Newton-King (the author of Json.NET) joined Microsoft and I’m pretty sure has been working with them to develop their JSON libraries.

      So it’s not really Microsoft going it alone – it’s Microsoft bringing “JSON.net” into their architecture as a standalone piece of code.

    • Jack Bond 1

      Get therapy nutjob. You sound like a deranged lunatic who’s still ranting because Windows 3.1 crashed. Question, if the performance numbers weren’t contrived, will you throw yourself off a building, and forever spare us the pathetic, tired, anti-Microsoft talking points?

      • Andrew Stanton 0

        I’ve crossed paths with Mr. Newton a few times online in the past and he’s always been pretty lucid, to the point, and correct in his observations and descriptions.
        You on the other hand were quick to judge without doing the rational thing of asking him for examples or data to back his claims and complaints. You read his frustration/anger/saltiness and completely misinterpreted it as some form of mental illness and tried to use it as a straw man to attack.
        Perhaps you should consider saying nothing if you or arent interested in hearing what someone is saying. 

        • Jack Bond 1

          “I bet your contrived examples were faster”
          Did he provide a SCINTILLA OF EVIDENCE to support that libel? No. Conclusion -> Filth
          “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.”
          What does the namespace have to do with how many libraries you deploy. NOTHING. Conclusion -> Laughably inexperienced.
          “Classic Microsoft “Not Invented Here” syndrome that plagues the entire MS infrastructure.”
          Outdated talking point at best. Conclusion -> Deranged bitter loser
          “Note how the worst security bugs are mostly Microsoft’s?”
          Laughably incorrect. Conclusion -> Lying filth
          His ENTIRE POST was lies and diatribes with no factual basis in reality. You know what call someone who operates in their own little universe. INSANE. And the normal course of action to treat that is therapy.

          • Eric Newton 0

            @Jack Bond I started the sentence with “I bet” which means I dont have evidence beyond my experience with Microsoft v1 products in general.  I specifically used “I bet” because I’m using my intuition.  Anybody can write a JSON serializer that would be 100 times faster than even System.Net.json where it only goes one level deep, doesnt respect type converters, etc.  That would be contrived.  Won’t work in the real world.
            It’s no secret that Microsoft frequently copies ideas from other software companies, the v1 is “revolutionary” but slow, v2 “fixes the problems” and v3 is the one actually useful in real production scenarios.
            My original point, which you seem to have lost in your dissertation, was that there’s already a perfectly good JSON serialization library that is tried and true, has extensve open source support, and is basically DE-FACTO standard of JSON serialization for the dotnet framework. 
            Introducing another set of types is ludicrous and will create the scenario where “old libraries” still use JSON.net and “new libraries” use this shadow of the original, and the types will not match.
            Here’s what they SHOULD have done.  Create a standardized set of INTERFACES for a JSON serializer for dotnet, and upgrade JSON.net to support those interfaces, so that JSON.net is still DE-FACTO library and is backward compatible with “old libraries”. 
            This piggybacks on the concept that the dotnet framework defauilt assembly binding needs to be relaxed (and Nuget actually does this with all those annoying binding redirects) to semver standards or at least binding on the “major” portion of the version and not all four octets of the assembly version.  I do wholeheartedly understand that this drastically changes how dotnet FRAMEWORK does binding, but dotnet CORE should’ve adopted this standard but they chose to stick to the incredibly annoying assembly binding redirects path.
            tl;dr system.net.json should just be a few interfaces for allowing asp.net core, web api, library X, library Y to all use an interchangable set of interfaces for serializing/deserializing and working with JSON data.  JSON.net should be the first library to implement it.  This is how you make thing work interchangably.  Not create your own, call it better, and then creating the “I use JSON.net” vs “I use System.text.json” flame wars.

        • Eric Newton 0

          @Andrew Stanton  Thank you for defending me. 
          It’s funny because people try to read speech inflections in text that can be quite different from how I intend them to be.
          I simply stated that a typical thing is to promote a contrived example that shows 1.5x speed increase over the existing library, without acknowledging that the new library may be fast, however it will not handle all the scenarios that have legitimately been baked into the existing library.
          Didnt ASP.Net Core claim huge speed increases for v1 and then the very next versions slowed down because it had to handle the messiness of real world HTTP protocol dealings?  That is also my point with System.Net.Json… in the end I bet the “speed increase” will be exactly the same as the existing JSON.net

      • Eric Newton 0

        @Jack Bond “get therapy nutjob” is quite beneath the quality of conversation while personally attacking me and adds zero to the conversation, and only makes YOU look like a deranged lunatic yourself,
        I’m pointing out something that everybody knows to be true… Microsoft subverts most software, slap it together, and the v1 sucks, the v2 still sucks but less, and the v3 is decent.  Azure Pipelines is the most recent case in point.

    • Peter Kese 1

      Eric Newton, you’re not adding anything usefull to this discussion besides your emotional diahreea. All of this had been widely discussed before; take your time and read the announcement at https://github.com/dotnet/corefx/issues/33115 — there’s also several quotes and posts of the author of Json.Net (JamesNK) explaining the reasoning.
      According to his own words:
      “Json.NET was created over 10 years ago, and since then it has added a wide range of features aimed to help developers work with JSON in .NET. In that time Json.NET has also become far and away NuGet’s most depended on and downloaded package, and is the go-to library for JSON support in .NET. Unfortunately, Json.NET’s wealth of features and popularity works against making major changes to it. Supporting new technologies like Span<T> would require fundamental breaking changes to the library and would disrupt existing applications and libraries that depend on it.
      Going forward Json.NET will continue to be worked on and invested in, both addressing known issues today and supporting new platforms in the future. Json.NET has always existed alongside other JSON libraries for .NET, and there will be nothing to prevent you using one or more together, depending on whether you need the performance of the new JSON APIs or the large feature set of Json.NET.”
      and also:
      “Json.NET isn’t going away. You aren’t losing anything. This is another option for simple and high performance scenarios.”

      • Eric Newton 0

        I’m going to ignore your first few sentences since they take personal shots at me, instead of addressing the core of the issue I was addressing. 
        Thanks for informing us of that issue on corefx since the blog poster here made zero mention of it.  Had he done so, perhaps some of us could have a more informed opinion about it.

        • Matthew Park 0

          You could also just inform yourself instead of making excuses that others didn’t inform you for you. The info isn’t hidden.

          • Carlos Villegas 0

            Second this.

    • Maxim Tsapov 0

      I used this serializer im my web applications. For me it has easier to use API, and i managed to writer a custom converter for byte arrays. Unfortunately the Json.NET and as well as the new System.Text.Json serialize byte arrays to Base64 strings which is a nuisance (and i think it’s an incorrect behavior). But a custom converter helped with this problem. It was easier to write it for System.Text.Json . And another good thing, that it (System.Text.Json) supports an async serialization.

      public class BytesConverter : JsonConverter<byte[]>
      {
      public override byte[] Read(
      ref Utf8JsonReader reader,
      Type typeToConvert,
      JsonSerializerOptions options) => reader.GetString()?.ConvertToBinary();

      public override void Write(
      Utf8JsonWriter writer,
      byte[] value, JsonSerializerOptions options)
      {
      writer.WriteStartArray();
      foreach (var val in value)
      {
      writer.WriteNumberValue(val);
      }
      writer.WriteEndArray();
      }
      }

      /// <summary>
      /// serialize an object to JSON
      /// </summary>
      public class Serializer : ISerializer
      {
      private static readonly JsonSerializerOptions Options = new JsonSerializerOptions();

      static Serializer()
      {
      Options.Converters.Add(new BytesConverter());
      }

      public string Serialize(object obj)
      {
      return JsonSerializer.Serialize(obj, Options);
      }

      public Task SerializeAsync<T>(T obj, Stream stream)
      {
      return JsonSerializer.SerializeAsync<T>(stream, obj, Options);
      }

      public object DeSerialize(string input, Type targetType)
      {
      return JsonSerializer.Deserialize(input, targetType, Options);
      }
      }

  • Greg Ingram 0

    Is the plan to create JSON schema APIs, as well?  Good stuff and great work team!

    • Jon Miller 0

      One would hope so given that Newtonsoft charges money for it. Given how ubiquitious JSON is, .NET should have JSON schema support by default, just like it supported XML schema before. However, many people disagree with me as I raised this issue on GitHub and everyone said that no one needs it and that it’s better to rely on random other NuGet libraries to do it that have no documentation.

  • PeteA 0

    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?

    • Eric Newton 0

      Regarding versioning, this isn’t really JSON.net’s fault, its the extremely strict assembly binding that is akin to old school COM interfaces from 20 years ago when dotnet framework started out.  I’ve been railing on this for a long time, and it got really exascerbated when Nuget came and would get libraries+dependencies.  We ended up back in the COM dll-hell days of incompatible binary interfaces carried forward into dotnet framework.  I found that ironic since the runtime has things like “MissingMethodException” thats thrown when the method signature cant be found on a type when a loose binding is used.
      For instance, a lot of applications use log4net with some using one particular build versus another for a time.  The only way to get two libraries to coexist with log4net v1 is via an assembly binding redirect that ends up effectively applying semver to the binding.
      Unfortunately NetCore seems to do the same thing, and IMO is the wrong move.  It should use a semver style assembly binding technique with specific assembly bindings used to sidestep problematic versions encountered somehow.

  • Daniel Morris 0

    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. 

    • Daniel Sturm 0

      Must be great not having to worry about leaving performance on the table for your APIs. 
      If you look a bit around though you’ll find that other people use protobuff or the JSON library by the stackoverflow people because while newtonsoft.json is very powerful and customisable its performance characteristics are not particularly great.
      Getting a high performance low level library for IO will benefit everyone in the long run. 

      • Eric Newton 0

        Thats a good point too.  However, I value correctness over speed.  System.net.json v1 might be speedier, but JSON.net is tried and true.

    • Eric Newton 0

      Excellent point.  A solution in search of a problem that didn’t exist until now. 
      Now there will be two competing type libraries de-facto JSON serialization libraries.  This is a step in the wrong direction.

      • Daniel Sturm 1

        You’re simply mistaken if you think this is not a problem currently. It might not be for your performance-insenstive use cases, which is fair – for most applications this is not that big of a deal. 
        But if you’re actually having to work on applications that require good scalability, Newtonsoft.Json is decidedly not the framework you’d be using anyhow. Here just one of those solutions to a non existing problem that’s in use on StackOverflow and other sites: https://github.com/kevin-montrose/Jil

    • Charles Roddie 0

      It’s good to have faster JSON serialization and deserialization in .Net Core. However if people really care about performance or serialization efficiency JSON is just the wrong format. It’s for when you need direct human readability and editing. E.g. in source control. CBOR is the best current choice when you don’t need direct human readability. JSON is way overused.

  • Vovchik Ivanov 0

    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?

  • Remy Reynold de Seresin 0

    What is the performance improvement on techempower JSON serialization benchmarks ?

  • Mark Adamson 0

    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

    • Mike-E 0

      We got your immutable objects covered here in case you want to return back to the fold, @Mark: https://github.com/wojtpl2/ExtendedXmlSerializer
      XML still natively supports comments as well… without any hacky booleans that technically break standards. 😉  Don’t forget about XSLT, etc.

      • Mark Adamson 0

        Amazing, thanks, looks neat! I’ll bear it in mind next time I work on some XML serialization. I too quite like XML and have used it happily lots before.

  • Robin Sue 0

    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.

    • Eric Newton 0

      Another excellent point, where the Microsofties build something in a cigar-smoke back room, declare it to be the fix for all the JSON serialization problems, and foist it out there with zero input.
      Maybe I missed it, but I don’t think anybody had any idea that in the banals of Microsoft, they realized JSON.net was too useful and needed to be replaced with another JSON serialization library.

  • Thad House 0

    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.

Feedback usabilla icon