{"id":31287,"date":"2020-12-16T09:00:37","date_gmt":"2020-12-16T16:00:37","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=31287"},"modified":"2020-12-18T23:31:16","modified_gmt":"2020-12-19T06:31:16","slug":"whats-next-for-system-text-json","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/whats-next-for-system-text-json\/","title":{"rendered":"What&#8217;s next for System.Text.Json?"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-5-0\/\">.NET 5.0 was released recently<\/a> and has come with many new features and performance improvements. <code>System.Text.Json<\/code> is no exception. We have improved performance, reliability, and made it easier to adopt for people who are familiar with <code>Newtonsoft.Json<\/code>. In this post, I&#8217;m going to talk about the progress that has been made with <code>System.Text.Json<\/code>, and what&#8217;s going to come next.<\/p>\n<h2>Getting the library<\/h2>\n<ul>\n<li>If your project is targeting .NET 5, <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/5.0\">install .NET 5<\/a>. This gives you <code>System.Text.Json<\/code> right out of the box, without any other pre-requisites.<\/li>\n<li>If your project is targeting .NET Core 3.x or .NET Framework, or if you&#8217;re writing a .NET Standard compatible library, install the latest <a href=\"https:\/\/www.nuget.org\/packages\/System.Text.Json\/\"><code>System.Text.Json<\/code> NuGet package<\/a>.<\/li>\n<li>If you&#8217;re starting a new ASP.NET Core project or upgrading an existing one from ASP.NET Core 2.2 to 3.0 or 5.0, <code>System.Text.Json<\/code> is the default serialization library being used under the covers.<\/li>\n<\/ul>\n<h2>What is <code>System.Text.Json<\/code>?<\/h2>\n<p><code>System.Text.Json<\/code> is the built-in JavaScript Object Notation (JSON) serialization library in .NET for converting from .NET object types to a JSON string, and vice versa, supporting UTF-8 text encoding. It was first added in .NET Core 3.0. A popular type in the library is <code>JsonSerializer<\/code>, which provides the highest level of functionality for processing JSON data. Here&#8217;s a simple example of how to use it to serialize and deserialize JSON to and from .NET object types:<\/p>\n<pre><code class=\"cs\">using System;\r\nusing System.Text.Json;\r\n\r\nMyType obj = new() { Message = \"Hello World!\" };\r\n\r\nstring json = JsonSerializer.Serialize(obj);\r\nConsole.WriteLine(json); \/\/ {\"Message\":\"Hello World!\"}\r\n\r\nMyType anotherObject = JsonSerializer.Deserialize&lt;MyType&gt;(\"{\"Message\":\"Hello Again.\"}\");\r\nConsole.WriteLine(anotherObject.Message); \/\/ \"Hello Again.\"\r\n\r\npublic class MyType\r\n{\r\n    public string Message { get; set; }\r\n}\r\n<\/code><\/pre>\n<h2>Why <code>System.Text.Json<\/code>? Looking back at .NET Core 3.x<\/h2>\n<p>Processing JSON data has become an essential part of many modern .NET applications, and in many cases, the format has even surpassed the usage of XML. However, .NET didn&#8217;t have a great built-in way to deal with JSON. Instead, users relied on <code>Newtonsoft.Json<\/code>, which continues to serve the .NET ecosystem well. It became clear that customers would benefit from a modern JSON library that came as part of the framework. Therefore, we built a new JSON library with the following considerations:<\/p>\n<ul>\n<li><strong>Provide high-performance JSON APIs<\/strong>. We needed a new set of JSON APIs that are highly tuned for performance, leverage modern additions to the framework like <code>Span&lt;T&gt;<\/code>, and can process UTF-8 directly without having to transcode to UTF-16 strings. These aspects are critical to ASP.NET Core, where throughput is a key requirement. We considered contributing changes to <code>Newtonsoft.Json<\/code>, but this was deemed infeasible without either breaking existing <code>Newtonsoft.Json<\/code> users or compromising on the performance we could achieve.<\/li>\n<li><strong>Remove the <code>Newtonsoft.Json<\/code> dependency from ASP.NET Core. Prior to .NET Core 3.x ASP.NET Core had a dependency on <code>Newtonsoft.Json<\/code><\/strong>. While this provided a tight integration between ASP.NET Core and <code>Newtonsoft.Json<\/code>, it also meant that the version of <code>Newtonsoft.Json<\/code> was dictated by the underlying platform. However, <code>Newtonsoft.Json<\/code> was frequently updated and application developers often needed to use a specific version. Thus, we wanted to remove the <code>Newtonsoft.Json<\/code> dependency from ASP.NET Core 3.0, so that customers could choose which version to use, without fearing they might accidentally break the underlying platform.<\/li>\n<\/ul>\n<p>Given the ubiquity of <code>Newtonsoft.Json<\/code> in the .NET ecosystem, we did not want to remove it as a dependency from ASP.NET Core without providing an easy way to add it back as the serialization mechanism. The way to do this is the following:<\/p>\n<ul>\n<li>Install the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.AspNetCore.Mvc.NewtonsoftJson\/\"><code>Microsoft.AspNetCore.Mvc.Newtonsoft.Json<\/code><\/a> NuGet package.<\/li>\n<li>Update <code>Startup.ConfigureServices()<\/code> to call <code>AddNewtonsoftJson()<\/code>.<\/li>\n<\/ul>\n<pre><code class=\"cs\">services.AddMvc()\r\n    .AddNewtonsoftJson();\r\n<\/code><\/pre>\n<p>For more information, see <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/migration\/22-to-30?view=aspnetcore-5.0&amp;tabs=visual-studio#newtonsoftjson-jsonnet-support\">Newtonsoft.Json (Json.NET) support<\/a>.<\/p>\n<h3>What we shipped in .NET Core 3.0<\/h3>\n<p>In .NET Core 3.0, we shipped the following types in the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json?view=net-5.0\"><code>System.Text.Json<\/code> namespace<\/a>:<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonserializer?view=net-5.0\"><code>JsonSerializer<\/code><\/a>: Provides functionality to serialize .NET objects to JSON representations and to deserialize JSON into .NET objects.<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsondocument?view=net-5.0\"><code>JsonDocument<\/code><\/a>: Provides random access capabilities for examining the structural content of a JSON value without automatically instantiating data values. This type is immutable.<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonelement?view=net-5.0\"><code>JsonElement<\/code><\/a>: Represents a specific JSON value within a <code>JsonDocument<\/code>.<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.utf8jsonwriter?view=net-5.0\"><code>Utf8JsonWriter<\/code><\/a>: Provides a high-performance API for forward-only, non-cached writing of UTF-8 encoded JSON text.<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.utf8jsonreader?view=net-5.0\"><code>Utf8JsonReader<\/code><\/a>: Provides a high-performance API for forward-only, token-by-token processing of UTF-8 encoded JSON text.<\/li>\n<\/ul>\n<p>In the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json?view=net-5.0\"><code>System.Text.Json.Serialization<\/code> namespace<\/a>, we shipped attributes and APIs for advanced scenarios and customization specific to serialization and deserialization with <code>JsonSerializer<\/code>. Popular among these is the <code>JsonConverter&lt;T&gt;<\/code> type which allows users to control the serialization and deserialization of a specific type, property, or field.<\/p>\n<p><code>JsonSerializer<\/code> provides the highest level of functionality for processing JSON data. Due to the wide feature breadth of implementing a serializer to convert between a persistent data format and .NET object types, <code>JsonSerializer<\/code> is undergoing far more development and receiving more community feedback than the other types in the JSON stack. As a result, I&#8217;ll mostly cover <code>JsonSerializer<\/code> functionality in this post.<\/p>\n<p>In 3.0, we shipped <code>JsonSerializer<\/code> with the following functionality:<\/p>\n<ul>\n<li>Support for serializing and deserializing plain old CLR objects (POCOs), primitives, and collections<\/li>\n<li>Built-in asynchronous serialization and deserialization<\/li>\n<li>Native processing of UTF-8 data<\/li>\n<li>Case-insensitive deserialization (in addition to case-sensitive)<\/li>\n<li>Camel-case naming policy with <code>JsonNamingPolicy.CamelCase<\/code><\/li>\n<li>Specifying custom naming policies with <code>JsonNamingPolicy<\/code><\/li>\n<li>Escaped JSON data deserialization<\/li>\n<li>Optional minimal character escaping on serialization<\/li>\n<li>Ignoring null values on serialization<\/li>\n<li>Ignoring comments on deserialization<\/li>\n<li>Allowing trailing commas<\/li>\n<li>Custom converters<\/li>\n<li>Indicating a property overflow bag when deserializing using <code>[JsonExtensionData]<\/code> attribute<\/li>\n<li>Ignoring individual properties using <code>[JsonIgnore]<\/code> attribute<\/li>\n<li>Specifying a custom property name using <code>[JsonProperty]<\/code> attribute<\/li>\n<\/ul>\n<p>See <a href=\"https:\/\/docs.microsoft.com\/dotnet\/standard\/serialization\/system-text-json-overview\">JSON serialization and deserialization (marshalling and unmarshalling) in .NET<\/a> for an overview of the functionality that <code>System.Text.Json<\/code> offers.<\/p>\n<h3>Choosing between <code>System.Text.Json<\/code> and <code>Newtonsoft.Json<\/code><\/h3>\n<h4>Feature differences<\/h4>\n<p>Now, you might be asking yourself, should I migrate from <code>Newtonsoft.Json<\/code> to use <code>System.Text.Json<\/code>? The answer, as you likely suspected is, it depends. In .NET Core 3.0, we made <code>System.Text.Json<\/code> the default serializer for ASP.NET Core because we believe it&#8217;s good enough for most applications. However, if you have heavily customized the serialization behavior by, for example, using several attributes from <code>Newtonsoft.Json<\/code>, you might find it more difficult to migrate as opposed to, say, someone who mostly uses POCOs. For more details, check out the documentation on <a href=\"https:\/\/docs.microsoft.com\/dotnet\/standard\/serialization\/system-text-json-migrate-from-newtonsoft-how-to\">How to migrate from Newtonsoft.Json to System.Text.Json<\/a>.<\/p>\n<p><code>System.Text.Json<\/code> focuses primarily on performance, security, and standards compliance, given it&#8217;s the default JSON-processing stack for new .NET applications. It has some key differences in default behavior and doesn&#8217;t aim to have feature parity with <code>Newtonsoft.Json<\/code>. For some scenarios, <code>System.Text.Json<\/code> has no built-in functionality, but there are recommended workarounds. For other scenarios, workarounds are impractical. If your application depends on a missing feature, consider <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/new\/choose\">filing an issue<\/a> to find out if support for your scenario can be added.<\/p>\n<p>That&#8217;s not to say that <code>System.Text.Json<\/code> doesn&#8217;t have features or flexibility, or that <code>Newtonsoft.Json<\/code> is slow; it&#8217;s just meant to illustrate the different design philosophies and how we resolve tension when features are in conflict with either performance or flexibility. The goal we have for <code>System.Text.Json<\/code> is to provide a fast built-in JSON stack that balances performance, security, and feature set. Every feature request is carefully weighed against these design principles. This may mean that new projects that align with these principles may more readily use <code>System.Text.Json<\/code>, whereas older projects may encounter more roadblocks during migration. However, there is nothing wrong with <code>Newtonsoft.Json<\/code>. If you&#8217;re happy with it, you should continue to use it.<\/p>\n<p>Let me provide two examples of features we don&#8217;t plan to add to <code>System.Text.Json<\/code> for the foreseeable future:<\/p>\n<ol>\n<li>Type conversion via <code>System.ComponentModel.TypeConverter<\/code><\/li>\n<li>Tweaking serialization behavior via <code>System.Runtime.Serialization<\/code> attributes<\/li>\n<\/ol>\n<p>Both of these are considered legacy systems from older serialization stacks, and supporting them would go against the performance-first architecture of <code>System.Text.Json<\/code> due to additional reflection-based lookups on start-up, and bring the maintenance and size burden of more code in the System.Text.Json.dll. Additionally, any feature that implies going against our principle of standards compliance, such as allowing processing of malformed JSON on deserialization, will not be accepted.<\/p>\n<p>We are designing <code>System.Text.Json<\/code> with extensibility in mind, meaning that even if the above features do not have native support within <code>JsonSerializer<\/code>, they can still be supported from user-provided configuration. For instance, users can write a wrapper around calls to <code>TypeConverter<\/code>s by using a custom <code>JsonConverter&lt;T&gt;<\/code>. We are also considering <code>IContractResolver<\/code> and <code>JsonProperty<\/code>-like mechanisms (from <code>Newtonsoft.Json<\/code>) to allow programmatic control of setting metadata and serialization logic for types and members (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/31257\">dotnet\/runtime #31257<\/a>), which would allow custom support for honoring <code>System.Runtime.Serialization<\/code> attributes.<\/p>\n<p><code>JsonSerializer<\/code> supports asynchronous serialization and deserialization of JSON data as a built-in, performant feature. This has been shown to provide significant value to customers writing highly scalable applications that need to be responsive and non-blocking, over <code>Newtonsoft.Json<\/code> which doesn&#8217;t have a built-in mechanism to support async JSON processing.<\/p>\n<p>The UTF-8 encoding is the de facto format for information transfer over the wire. <code>System.Text.Json<\/code> APIs natively process data with this encoding and do not need to transcode to and from UTF-16, unlike <code>Newtonsoft.Json<\/code>. Callers also do not need to manually transcode data before passing it as input to the <code>System.Text.Json<\/code> APIs. Avoiding this transcoding also helps yield better performance when processing JSON data. Note that <code>string<\/code>-based (UTF-16) APIs are also available as a convenience but incur the cost of transcoding.<\/p>\n<h4>Performance<\/h4>\n<p>During the .NET Core 3.0 development cycle, we <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/try-the-new-system-text-json-apis\/\">blogged<\/a> about how <code>System.Text.Json<\/code> performance compares with <code>Newtonsoft.Json<\/code>:<\/p>\n<table>\n<thead>\n<tr>\n<th>Scenario<\/th>\n<th>Speed<\/th>\n<th>Memory<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Deserialization<\/td>\n<td>2x faster<\/td>\n<td>Parity or lower<\/td>\n<\/tr>\n<tr>\n<td>Serialization<\/td>\n<td>1.5x faster<\/td>\n<td>Parity or lower<\/td>\n<\/tr>\n<tr>\n<td>Document (read-only)<\/td>\n<td>3-5x faster<\/td>\n<td>~Allocation free for sizes &lt; 1 MB<\/td>\n<\/tr>\n<tr>\n<td>Reader<\/td>\n<td>2-3x faster<\/td>\n<td>~Allocation free (until you materialize values)<\/td>\n<\/tr>\n<tr>\n<td>Writer<\/td>\n<td>1.3-1.6x faster<\/td>\n<td>~Allocation free<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>As it is with any performance benchmarks, scenarios vary, and summarizing numbers is nuanced. The summary of that post read:<\/p>\n<blockquote><p>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&#8217;s important to you.<\/p><\/blockquote>\n<p>Some people took this to mean that <code>System.Text.Json<\/code> is always twice as fast, which wasn&#8217;t the intended takeaway. The &#8220;up to&#8221; and &#8220;depends on your scenario and your payload&#8221; statements are key in that sentence.<\/p>\n<p>There have been multiple performance comparisons between <code>JsonSerializer<\/code> and other serializers in the .NET ecosystem. You may have noticed that <code>JsonSerializer<\/code> is not always the fastest. You might wonder why that is when we have said that performance is a primary goal. In software, almost no capability can be maximized without hurting other capabilities, so all functionality is a trade-off between various aspects. And while performance of JSON serialization is very important to us, it&#8217;s not the only goal we have. Other goals include security, reliability, standards compliance, and usability.<\/p>\n<p>Here are a few examples of some trade-offs we made against better performance:<\/p>\n<ul>\n<li><strong>Using classes in some cases<\/strong>. We made the decision to turn <code>Utf8JsonWriter<\/code> into a class. It used to be a <code>ref struct<\/code> which allowed for fewer allocations and faster access to the underlying span we&#8217;re writing to. We changed it to a <code>class<\/code> to avoid problems where developers accidentally create copies of the writer (for example, when passing it to a helper method by value) and subsequent writes end up overwriting data in the buffer.<\/li>\n<li><strong>Validation<\/strong>. We made the decision to validate JSON data when it&#8217;s being processed, which includes both UTF-8 validation as well as JSON syntax validation. This is necessary to enable server and cloud scenarios where the JSON input is often coming from untrusted sources.<\/li>\n<li><strong>Extension points<\/strong>. The serializer has the ability to register custom converters. These indirections, while relatively cheap, are costly when running in a tight loop; sometimes even when they aren&#8217;t used (due to lookups).<\/li>\n<\/ul>\n<h2>What&#8217;s new in .NET 5.0?<\/h2>\n<h3>New features<\/h3>\n<p>In .NET 5.0, we have implemented some of the most sought after features from the community and made it easier to adopt for people who are familiar with <code>Newtonsoft.Json<\/code>. Here is an overview of the features that were added:<\/p>\n<table>\n<thead>\n<tr>\n<th>GitHub Issue<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30820\">#30820<\/a><\/td>\n<td>Add mechanism to preserve object references when (de)serializing<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/32937\">#32937<\/a><\/td>\n<td>Add extension methods for <code>HttpClient<\/code> and <code>HttpContent<\/code> that allow (de)serializing JSON<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30255\">#30255<\/a><\/td>\n<td>Support (de)serializing quoted numbers<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/29895\">#29895<\/a><\/td>\n<td>Support deserializing objects using parameterized constructors<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/876\">#876<\/a><\/td>\n<td>Support (de)serializing fields<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/779\">#779<\/a><\/td>\n<td>Support ignoring value-type defaults<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30687\">#30687<\/a><\/td>\n<td>Support conditionally ignoring properties (always, never, when null\/default)<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30524\">#30524<\/a><\/td>\n<td>Support non-string dictionary keys<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/29743\">#29743<\/a><\/td>\n<td>Allow using non-public property accessors for (de)serialization<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/34439\">#34439<\/a><\/td>\n<td>Provide opt-in for custom converters to handle null<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/38539\">#38539<\/a><\/td>\n<td>Support new C# record types<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/30445\">#30445<\/a><\/td>\n<td>Add a copy constructor to JsonSerializerOptions<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/34626\">#34626<\/a><\/td>\n<td>Add constructor to JsonSerializerOptions that takes serialization defaults<\/td>\n<\/tr>\n<tr>\n<td><a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/31326\">#31326<\/a><\/td>\n<td>Enable JsonSerializer to work on Xamarin iOS\/Android<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When we announced <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-5-0\/#system-text-json\">.NET 5.0<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-5-0-rc-1\/#system-text-json\">.NET 5.0 RC 1<\/a>, we went over some of the new features, including improved support for immutable types and preserving references in JSON object graphs. In this section, I will go over a few more.<\/p>\n<h4>Support for quoted numbers<\/h4>\n<p>When .NET Core 3.0 shipped, deserializing number types encoded as JSON strings was not supported by default. The workaround was to add a custom converter for each applicable number type which would take control of the serialization and deserialization of the type, and include logic to handle quoted numbers. In .NET 5, we have added a convenient, opt-in feature to support serializing and deserializing quoted numbers, and named floating point literals (<code>NaN<\/code>, <code>Infinity<\/code>, and <code>-Infinity<\/code>). Here is an example that uses this feature:<\/p>\n<pre><code class=\"cs\">using System;\r\nusing System.Text.Json;\r\nusing System.Text.Json.Serialization;\r\n\r\nvar options = new JsonSerializerOptions\r\n{\r\n    NumberHandling = JsonNumberHandling.AllowReadingFromString | \r\n        JsonNumberHandling.WriteAsString\r\n};\r\n\r\nstring json = @\"{\"\"NumberOne\"\":1,\"\"NumberTwo\"\":\"\"2\"\"}\";\r\n\r\nClassWithInts @class = JsonSerializer.Deserialize&lt;ClassWithInts&gt;(json, options);\r\nConsole.WriteLine(@class.NumberOne); \/\/ 1\r\nConsole.WriteLine(@class.NumberTwo); \/\/ 2\r\n\r\njson = JsonSerializer.Serialize(@class, options);\r\nConsole.WriteLine(json); \/\/ @\"{\"\"NumberOne\"\":\"\"1\"\",\"\"NumberTwo\"\":\"\"2\"\"}\";\r\n\r\npublic class ClassWithInts\r\n{\r\n    public int NumberOne { get; set; }\r\n    public int NumberTwo { get; set; }\r\n}\r\n<\/code><\/pre>\n<p>In addition to the globally applied <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonserializeroptions.numberhandling?view=net-5.0\"><code>JsonSerializerOptions.NumberHandling<\/code><\/a> option, we have also added <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.serialization.jsonnumberhandlingattribute?view=net-5.0\"><code>JsonNumberHandlingAttribute<\/code><\/a> which enables specifying number handling settings for a type, property, or field.<\/p>\n<h4>Extended support for ignoring <code>default<\/code> values<\/h4>\n<p>When serializing in .NET Core 3.x, it was only possible to ignore <code>null<\/code> values for reference or nullable value-typed properties, and the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonserializeroptions.ignorenullvalues?view=net-5.0\"><code>JsonSerializerOptions.IgnoreNullValues<\/code><\/a> setting was only applicable across an entire input object graph. In .NET 5, we added support to ignore <code>default<\/code> values for non-nullable value types, such as <code>int<\/code> and <code>float<\/code>. Furthermore, it is now possible to cherry-pick properties and fields to be ignored when <code>default<\/code>. See the following example:<\/p>\n<pre><code class=\"cs\">var options = new JsonSerializerOptions\r\n{\r\n    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault\r\n};\r\n\r\nvar obj = new MyClass();\r\nstring json = JsonSerializer.Serialize(obj, options);\r\nConsole.WriteLine(json); \/\/ {\"MyBool\":false}\r\n\r\npublic class MyClass\r\n{\r\n    public int MyInt { get; set; }\r\n\r\n    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]\r\n    public bool MyBool { get; set; }\r\n\r\n    public string MyString { get; set; }\r\n}\r\n<\/code><\/pre>\n<p>Here we see a combination of the new features: we tell the serializer to ignore <code>default<\/code> values across the entire object graph, but indicate that one of the properties should never be ignored, regardless of the global option. To only ignore <code>null<\/code> values, but not <code>default<\/code> values for value types, use <code>JsonIgnoreCondition.WhenWritingNull<\/code>.<\/p>\n<p>With these augmentations to handling <code>default<\/code> values, the <code>JsonSerializerOptions.IgnoreNullValues<\/code> property has been made obsolete in the upcoming .NET 6.0 release. <code>IgnoreNullValues<\/code> is honored when deserializing as well as when serializing. This is not desired, as there is no good reason for ignoring <code>null<\/code> tokens in input payloads.<\/p>\n<h4>Utility constructors for <code>JsonSerializerOptions<\/code><\/h4>\n<p>Copying <code>JsonSerializerOptions<\/code> settings from one instance to another, then making a few mutations, is a common operation. In .NET 5, we added a copy constructor to <code>JsonSerializerOptions<\/code> which makes this easier:<\/p>\n<pre><code class=\"cs\">JsonSerializerOptions options = new()\r\n{\r\n    NumberHandling = JsonNumberHandling.AllowReadingFromString,\r\n    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,\r\n    Converters = { new JsonStringEnumConverter() }\r\n};\r\n\r\nJsonSerializerOptions newOptions = new(options)\r\n{\r\n    NumberHandling = JsonNumberHandling.Strict\r\n};\r\n\r\nConsole.WriteLine(newOptions.NumberHandling);\r\n\/\/ Strict\r\nConsole.WriteLine(newOptions.DefaultIgnoreCondition);\r\n\/\/ WhenWritingNull\r\nConsole.WriteLine(newOptions.Converters[0]);\r\n\/\/ System.Text.Json.Serialization.JsonStringEnumConverter\r\n<\/code><\/pre>\n<p>A common set of options when processing JSON data in web contexts is to use the <code>camelCase<\/code> naming policy, specify case-insensitive matching when deserializing, and allow reading quoted numbers. The new <a href=\"https:\/\/docs.microsoft.com\/dotnet\/api\/system.text.json.jsonserializerdefaults?view=net-5.0#fields\"><code>JsonSerializerDefaults.Web<\/code><\/a> enum value, along with a new constructor that takes a <code>JsonSerializerDefaults<\/code> value, provides an easy way to reuse these options in a consistent way across multiple layers of an application, (e.g. client, shared, and server for Blazor scenarios). Let&#8217;s take a look:<\/p>\n<pre><code class=\"cs\">JsonSerializerOptions options = new(JsonSerializerDefaults.Web);\r\nConsole.WriteLine(options.PropertyNamingPolicy);\r\n\/\/ System.Text.Json.JsonCamelCaseNamingPolicy\r\nConsole.WriteLine(options.PropertyNameCaseInsensitive);\r\n\/\/ True\r\nConsole.WriteLine(options.NumberHandling);\r\n\/\/ AllowReadingFromString\r\n<\/code><\/pre>\n<p>For more information on what features <code>JsonSerializer<\/code> supports, in comparison to <code>Newtonsoft.Json<\/code>, check out <a href=\"https:\/\/docs.microsoft.com\/dotnet\/standard\/serialization\/system-text-json-migrate-from-newtonsoft-how-to#table-of-differences-between-newtonsoftjson-and-systemtextjson\">this table from the migration guide<\/a>.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/migration-guide.png\" alt=\"Migration Guide\" \/><\/p>\n<h3>Performance improvements<\/h3>\n<p>Between .NET Core 3.1 and .NET 5, we have improved performance in the following areas:<\/p>\n<ul>\n<li>Improved serialization and deserialization performance for collections<\/li>\n<li>Improved serialization and deserialization performance for small types<\/li>\n<li>Improved deserialization performance for case-insensitive and missing-property cases<\/li>\n<li>Improved serialization performance for long JSON strings<\/li>\n<\/ul>\n<p>These improvements are going to be especially meaningful for high-performance apps.<\/p>\n<h4>Improved performance for collections<\/h4>\n<p>We made significant improvements for large collections (<strong>~1.15x-1.5x<\/strong> on deserialize, <strong>~1.5x-2.4x<\/strong> on serialize). You can see these improvements characterized in much more detail at <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/2259\">dotnet\/runtime #2259<\/a>. The following numbers show performance numbers for processing collections with 1,024 elements.<\/p>\n<p><strong><code>Dictionary&lt;string, string&gt;<\/code><\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th align=\"right\">Mean<\/th>\n<th align=\"right\">Error<\/th>\n<th align=\"right\">StdDev<\/th>\n<th align=\"right\">Median<\/th>\n<th align=\"right\">Min<\/th>\n<th align=\"right\">Max<\/th>\n<th align=\"right\">Gen 0<\/th>\n<th align=\"right\">Gen 1<\/th>\n<th align=\"right\">Gen 2<\/th>\n<th align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Deserialize Before<\/td>\n<td align=\"right\">190.4 us<\/td>\n<td align=\"right\">1.47 us<\/td>\n<td align=\"right\">1.38 us<\/td>\n<td align=\"right\">190.6 us<\/td>\n<td align=\"right\">188.5 us<\/td>\n<td align=\"right\">193.6 us<\/td>\n<td align=\"right\">26.5554<\/td>\n<td align=\"right\">8.3460<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">163.69 KB<\/td>\n<\/tr>\n<tr>\n<td>After <strong>~1.2x faster<\/strong><\/td>\n<td align=\"right\">158.8 us<\/td>\n<td align=\"right\">1.27 us<\/td>\n<td align=\"right\">1.13 us<\/td>\n<td align=\"right\">158.8 us<\/td>\n<td align=\"right\">157.3 us<\/td>\n<td align=\"right\">161.3 us<\/td>\n<td align=\"right\">26.5991<\/td>\n<td align=\"right\">8.8664<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">164.05 KB<\/td>\n<\/tr>\n<tr>\n<td>Serialize Before<\/td>\n<td align=\"right\">109.7 us<\/td>\n<td align=\"right\">0.77 us<\/td>\n<td align=\"right\">0.72 us<\/td>\n<td align=\"right\">109.5 us<\/td>\n<td align=\"right\">108.5 us<\/td>\n<td align=\"right\">111.1 us<\/td>\n<td align=\"right\">3.4904<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">23.92 KB<\/td>\n<\/tr>\n<tr>\n<td>After <strong>~1.5x faster<\/strong><\/td>\n<td align=\"right\">74.53 us<\/td>\n<td align=\"right\">0.590 us<\/td>\n<td align=\"right\">0.552 us<\/td>\n<td align=\"right\">74.40 us<\/td>\n<td align=\"right\">73.57 us<\/td>\n<td align=\"right\">75.69 us<\/td>\n<td align=\"right\">3.8179<\/td>\n<td align=\"right\">0.2937<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">24.17 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong><code>List&lt;int&gt;<\/code><\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th align=\"right\">Mean<\/th>\n<th align=\"right\">Error<\/th>\n<th align=\"right\">StdDev<\/th>\n<th align=\"right\">Median<\/th>\n<th align=\"right\">Min<\/th>\n<th align=\"right\">Max<\/th>\n<th align=\"right\">Gen 0<\/th>\n<th align=\"right\">Gen 1<\/th>\n<th align=\"right\">Gen 2<\/th>\n<th align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Deserialize Before<\/td>\n<td align=\"right\">76.40 us<\/td>\n<td align=\"right\">0.392 us<\/td>\n<td align=\"right\">0.366 us<\/td>\n<td align=\"right\">76.37 us<\/td>\n<td align=\"right\">75.53 us<\/td>\n<td align=\"right\">76.87 us<\/td>\n<td align=\"right\">1.2169<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">8.25 KB<\/td>\n<\/tr>\n<tr>\n<td>After <strong>~1.5x faster<\/strong><\/td>\n<td align=\"right\">50.05 us<\/td>\n<td align=\"right\">0.251 us<\/td>\n<td align=\"right\">0.235 us<\/td>\n<td align=\"right\">49.94 us<\/td>\n<td align=\"right\">49.76 us<\/td>\n<td align=\"right\">50.43 us<\/td>\n<td align=\"right\">1.3922<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">8.62 KB<\/td>\n<\/tr>\n<tr>\n<td>Serialize Before<\/td>\n<td align=\"right\">29.04 us<\/td>\n<td align=\"right\">0.213 us<\/td>\n<td align=\"right\">0.189 us<\/td>\n<td align=\"right\">29.00 us<\/td>\n<td align=\"right\">28.70 us<\/td>\n<td align=\"right\">29.34 us<\/td>\n<td align=\"right\">1.2620<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">8.07 KB<\/td>\n<\/tr>\n<tr>\n<td>After <strong>~2.4x faster<\/strong><\/td>\n<td align=\"right\">12.17 us<\/td>\n<td align=\"right\">0.205 us<\/td>\n<td align=\"right\">0.191 us<\/td>\n<td align=\"right\">12.15 us<\/td>\n<td align=\"right\">11.97 us<\/td>\n<td align=\"right\">12.55 us<\/td>\n<td align=\"right\">1.3187<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">8.34 KB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4><\/h4>\n<h4>Improved performance for small types (TechEmpower benchmarks)<\/h4>\n<p>In .NET 5.0, there was a significant effort to improve .NET performance in the <a href=\"https:\/\/www.techempower.com\/benchmarks\/\">TechEmpower<\/a> JSON benchmark. This work spanned across multiple areas, including the networking stack, Kestrel, and <code>JsonSerializer<\/code> itself. As a result, performance when observing work done within the serializer is now <strong>~19% better<\/strong>.<\/p>\n<p>These changes, and the performance measurements are covered in detail at <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/37976\">dotnet\/runtime #37976<\/a>. There are two sets of benchmarks there. The first is validating performance with the <a href=\"https:\/\/github.com\/dotnet\/performance\/tree\/master\/src\/benchmarks\/micro\/libraries\/System.Text.Json\/Serializer\"><code>JsonSerializer<\/code> performance benchmarks<\/a> that the team maintains. There is an <strong>~8% improvement<\/strong> observed. The next section is for TechEmpower. It measures three different approaches for satisfying the requirements of the TechEmpower JSON benchmark. <code>SerializeWithCachedBufferAndWriter<\/code> is the one we use in the <a href=\"https:\/\/github.com\/aspnet\/Benchmarks\/blob\/4e58073ac06642b34c25119c73ef22932c1ece64\/src\/BenchmarksApps\/Kestrel\/PlatformBenchmarks\/BenchmarkApplication.Json.cs#L29-L33\">official benchmark<\/a>.<\/p>\n<table style=\"width: 84.915%; height: 109px;\">\n<thead>\n<tr style=\"height: 28px;\">\n<th style=\"height: 28px; width: 44.7346%;\">Method<\/th>\n<th style=\"height: 28px; width: 2.94861%;\" align=\"right\">Mean<\/th>\n<th style=\"height: 28px; width: 5.56024%;\" align=\"right\">Error<\/th>\n<th style=\"height: 28px; width: 5.56024%;\" align=\"right\">StdDev<\/th>\n<th style=\"height: 28px; width: 6.48694%;\" align=\"right\">Median<\/th>\n<th style=\"height: 28px; width: 6.48694%;\" align=\"right\">Min<\/th>\n<th style=\"height: 28px; width: 6.48694%;\" align=\"right\">Max<\/th>\n<th style=\"height: 28px; width: 5.22325%;\" align=\"right\">Gen 0<\/th>\n<th style=\"height: 28px; width: 4.71778%;\" align=\"right\">Gen 1<\/th>\n<th style=\"height: 28px; width: 4.71778%;\" align=\"right\">Gen 2<\/th>\n<th style=\"height: 28px; width: 6.99242%;\" align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 28px;\">\n<td style=\"height: 28px; width: 44.7346%;\">SerializeWithCachedBufferAndWriter (Before)<\/td>\n<td style=\"height: 28px; width: 2.94861%;\" align=\"right\">155.3 ns<\/td>\n<td style=\"height: 28px; width: 5.56024%;\" align=\"right\">1.19 ns<\/td>\n<td style=\"height: 28px; width: 5.56024%;\" align=\"right\">1.11 ns<\/td>\n<td style=\"height: 28px; width: 6.48694%;\" align=\"right\">155.5 ns<\/td>\n<td style=\"height: 28px; width: 6.48694%;\" align=\"right\">153.3 ns<\/td>\n<td style=\"height: 28px; width: 6.48694%;\" align=\"right\">157.3 ns<\/td>\n<td style=\"height: 28px; width: 5.22325%;\" align=\"right\">0.0038<\/td>\n<td style=\"height: 28px; width: 4.71778%;\" align=\"right\">&#8211;<\/td>\n<td style=\"height: 28px; width: 4.71778%;\" align=\"right\">&#8211;<\/td>\n<td style=\"height: 28px; width: 6.99242%;\" align=\"right\">24 B<\/td>\n<\/tr>\n<tr style=\"height: 53px;\">\n<td style=\"height: 53px; width: 44.7346%;\">SerializeWithCachedBufferAndWriter (After) <strong>~1.19x faster<\/strong><\/td>\n<td style=\"height: 53px; width: 2.94861%;\" align=\"right\">130.8 ns<\/td>\n<td style=\"height: 53px; width: 5.56024%;\" align=\"right\">1.50 ns<\/td>\n<td style=\"height: 53px; width: 5.56024%;\" align=\"right\">1.40 ns<\/td>\n<td style=\"height: 53px; width: 6.48694%;\" align=\"right\">130.9 ns<\/td>\n<td style=\"height: 53px; width: 6.48694%;\" align=\"right\">128.6 ns<\/td>\n<td style=\"height: 53px; width: 6.48694%;\" align=\"right\">133.0 ns<\/td>\n<td style=\"height: 53px; width: 5.22325%;\" align=\"right\">0.0037<\/td>\n<td style=\"height: 53px; width: 4.71778%;\" align=\"right\">&#8211;<\/td>\n<td style=\"height: 53px; width: 4.71778%;\" align=\"right\">&#8211;<\/td>\n<td style=\"height: 53px; width: 6.99242%;\" align=\"right\">24 B<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>All the work done across the stack will improve the placement of .NET on the JSON TechEmpower benchmark once we update our entries to .NET 5.0. We can compare the performance of the <code>aspcore<\/code> entry of the benchmark in .NET 3.1 (<a href=\"https:\/\/www.techempower.com\/benchmarks\/#section=data-r19&amp;hw=ph&amp;test=json\">Round 19, 2020-05-28<\/a>) to .NET 5.0 (<a href=\"https:\/\/www.techempower.com\/benchmarks\/#section=test&amp;runid=996d0239-a18a-4054-b496-3506868f0723&amp;hw=ph&amp;test=json&amp;a=2\">Unofficial continuous run, 2020-12-14<\/a>):<\/p>\n<p><strong>Before<\/strong><\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/techempower-before.png\" alt=\"TechEmpower Before\" \/><\/p>\n<p><strong>After<\/strong><\/p>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/12\/techempower-after.png\" alt=\"TechEmpower After\" \/><\/p>\n<p>We can see that the responses per second (RPS) for JSON serialization increased from 904,846 to 1,190,245, which is a <strong>~31% improvement<\/strong>.<\/p>\n<h4>Improved deserialization performance for case-insensitive and extra-property cases<\/h4>\n<p>One of the most common issues with using JSON is a mismatch of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Naming_convention_(programming)#Letter_case-separated_words\">naming conventions<\/a> with .NET design guidelines. JSON properties are often <a href=\"https:\/\/en.wikipedia.org\/wiki\/Camel_case\">camelCase<\/a> and .NET properties and fields are typically PascalCase. The JSON serializer you use is responsible for bridging between naming conventions. That doesn&#8217;t come for free, at least not with .NET Core 3.1. That cost is now negligible with .NET 5.0.<\/p>\n<p>The code that allows for case insensitivity and extra properties has been greatly improved in .NET 5.0. It is <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/35848\"><strong>~1.75x faster<\/strong> in some cases<\/a>.<\/p>\n<p>The following benchmarks are for a simple 4-property test class that has property names which have more than 7 characters.<\/p>\n<p><strong>3.1 performance<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th align=\"right\">Mean<\/th>\n<th align=\"right\">Error<\/th>\n<th align=\"right\">StdDev<\/th>\n<th align=\"right\">Median<\/th>\n<th align=\"right\">Min<\/th>\n<th align=\"right\">Max<\/th>\n<th align=\"right\">Gen 0<\/th>\n<th align=\"right\">Gen 1<\/th>\n<th align=\"right\">Gen 2<\/th>\n<th align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>CaseSensitive_Matching<\/td>\n<td align=\"right\">844.2 ns<\/td>\n<td align=\"right\">4.25 ns<\/td>\n<td align=\"right\">3.55 ns<\/td>\n<td align=\"right\">844.2 ns<\/td>\n<td align=\"right\">838.6 ns<\/td>\n<td align=\"right\">850.6 ns<\/td>\n<td align=\"right\">0.0342<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">224 B<\/td>\n<\/tr>\n<tr>\n<td>CaseInsensitive_Matching<\/td>\n<td align=\"right\">833.3 ns<\/td>\n<td align=\"right\">3.84 ns<\/td>\n<td align=\"right\">3.40 ns<\/td>\n<td align=\"right\">832.6 ns<\/td>\n<td align=\"right\">829.4 ns<\/td>\n<td align=\"right\">841.1 ns<\/td>\n<td align=\"right\">0.0504<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">328 B<\/td>\n<\/tr>\n<tr>\n<td>CaseSensitive_NotMatching (Extra)<\/td>\n<td align=\"right\">1,007.7 ns<\/td>\n<td align=\"right\">9.40 ns<\/td>\n<td align=\"right\">8.79 ns<\/td>\n<td align=\"right\">1,005.1 ns<\/td>\n<td align=\"right\">997.3 ns<\/td>\n<td align=\"right\">1,023.3 ns<\/td>\n<td align=\"right\">0.0722<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">464 B<\/td>\n<\/tr>\n<tr>\n<td>CaseInsensitive_NotMatching<\/td>\n<td align=\"right\">1,405.6 ns<\/td>\n<td align=\"right\">8.35 ns<\/td>\n<td align=\"right\">7.40 ns<\/td>\n<td align=\"right\">1,405.1 ns<\/td>\n<td align=\"right\">1,397.1 ns<\/td>\n<td align=\"right\">1,423.6 ns<\/td>\n<td align=\"right\">0.0626<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">408 B<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><strong>5.0 performance<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th align=\"right\">Mean<\/th>\n<th align=\"right\">Error<\/th>\n<th align=\"right\">StdDev<\/th>\n<th align=\"right\">Median<\/th>\n<th align=\"right\">Min<\/th>\n<th align=\"right\">Max<\/th>\n<th align=\"right\">Gen 0<\/th>\n<th align=\"right\">Gen 1<\/th>\n<th align=\"right\">Gen 2<\/th>\n<th align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>CaseSensitive_Matching<\/td>\n<td align=\"right\">799.2 ns<\/td>\n<td align=\"right\">4.59 ns<\/td>\n<td align=\"right\">4.29 ns<\/td>\n<td align=\"right\">801.0 ns<\/td>\n<td align=\"right\">790.5 ns<\/td>\n<td align=\"right\">803.9 ns<\/td>\n<td align=\"right\">0.0985<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">632 B<\/td>\n<\/tr>\n<tr>\n<td>CaseInsensitive_Matching<\/td>\n<td align=\"right\">789.2 ns<\/td>\n<td align=\"right\">6.62 ns<\/td>\n<td align=\"right\">5.53 ns<\/td>\n<td align=\"right\">790.3 ns<\/td>\n<td align=\"right\">776.0 ns<\/td>\n<td align=\"right\">794.4 ns<\/td>\n<td align=\"right\">0.1004<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">632 B<\/td>\n<\/tr>\n<tr>\n<td>CaseSensitive_NotMatching (Extra)<\/td>\n<td align=\"right\">479.9 ns<\/td>\n<td align=\"right\">0.75 ns<\/td>\n<td align=\"right\">0.59 ns<\/td>\n<td align=\"right\">479.8 ns<\/td>\n<td align=\"right\">479.1 ns<\/td>\n<td align=\"right\">481.0 ns<\/td>\n<td align=\"right\">0.0059<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">40 B<\/td>\n<\/tr>\n<tr>\n<td>CaseInsensitive_NotMatching<\/td>\n<td align=\"right\">783.5 ns<\/td>\n<td align=\"right\">3.26 ns<\/td>\n<td align=\"right\">2.89 ns<\/td>\n<td align=\"right\">783.5 ns<\/td>\n<td align=\"right\">779.0 ns<\/td>\n<td align=\"right\">789.2 ns<\/td>\n<td align=\"right\">0.1004<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">632 B<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h4>Improve serialization performance for long JSON strings<\/h4>\n<p><a href=\"https:\/\/github.com\/dotnet\/corefx\/pull\/41845\">dotnet\/corefx #41845<\/a> utilized SSE2 intrinsics to make the security check to see if a JSON string needs escaping, faster. When serializing commonly found payloads, there is a <strong>~10-20% improvement<\/strong>. Also, when writing relatively large JSON strings using the writer directly, there is a <strong>~30% improvement<\/strong>. The performance characteristics of this change are discussed in greater detail in this <a href=\"https:\/\/gist.github.com\/ahsonkhan\/c566f5e7d65c1fde5a83a67be290c4ee\">GitHub gist<\/a>. One interesting data point is a benchmark that shows improvements when serializing an instance of a POCO representing a NuGet search result:<\/p>\n<p><strong>Before:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th>Method<\/th>\n<th align=\"right\">Mean<\/th>\n<th align=\"right\">Error<\/th>\n<th align=\"right\">StdDev<\/th>\n<th align=\"right\">Median<\/th>\n<th align=\"right\">Min<\/th>\n<th align=\"right\">Max<\/th>\n<th align=\"right\">Gen 0<\/th>\n<th align=\"right\">Gen 1<\/th>\n<th align=\"right\">Gen 2<\/th>\n<th align=\"right\">Allocated<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>SerializeNugetPayload (Before)<\/td>\n<td align=\"right\">791.7 ms<\/td>\n<td align=\"right\">15.69 ms<\/td>\n<td align=\"right\">16.12 ms<\/td>\n<td align=\"right\">787.4 ms<\/td>\n<td align=\"right\">772.0 ms<\/td>\n<td align=\"right\">827.1 ms<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">787.3 MB<\/td>\n<\/tr>\n<tr>\n<td>SerializeNugetPayload (After) <strong>~1.13x faster<\/strong><\/td>\n<td align=\"right\">698.4 ms<\/td>\n<td align=\"right\">6.63 ms<\/td>\n<td align=\"right\">5.53 ms<\/td>\n<td align=\"right\">699.5 ms<\/td>\n<td align=\"right\">690.9 ms<\/td>\n<td align=\"right\">708.4 ms<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">&#8211;<\/td>\n<td align=\"right\">787.3 MB<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>Breaking changes<\/h3>\n<p>We made a few behavioral breaking changes between .NET Core 3.x and .NET 5.0. Given that <code>System.Text.Json<\/code> is a platform component and adheres to a strict compatibility standard like the rest of .NET, these changes were made based on careful evaluation of their impacts, and serve to improve the library as a whole. Though important, these changes generally address edge cases, especially around consistency, and should have minimal impact to the vast majority of folks using the library. For more significant behavioral changes and new feature additions, we make them opt-in to avoid breaking existing code written against previous versions of the library. See the linked documents for more information on each breaking change.<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/serialization\/5.0\/deserializing-json-into-char-requires-single-character\">Deserialization of <code>char<\/code> types requires a single-character string<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/serialization\/5.0\/jsonserializer-allows-reading-numbers-as-strings\">ASP.NET Core apps allow deserializing quoted numbers<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/serialization\/5.0\/jsonserializer-serialize-throws-argumentnullexception-for-null-type\"><code>JsonSerializer.Serialize<\/code> throws <code>ArgumentNullException<\/code> when <code>Type<\/code> parameter is null<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/serialization\/5.0\/non-public-parameterless-constructors-not-used-for-deserialization\">Non-<code>public<\/code>, parameterless constructors not used for deserialization<\/a><\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/serialization\/5.0\/options-honored-when-serializing-key-value-pairs\">PropertyNamingPolicy, PropertyNameCaseInsensitive, and Encoder options are honored when serializing and deserializing <code>KeyValuePair&lt;TKey,TValue&gt;<\/code><\/a><\/li>\n<\/ul>\n<h2>What&#8217;s next for System.Text.Json?<\/h2>\n<p>We have started planning for .NET 6.0. We&#8217;ll continue to address the most requested features that help drive <code>System.Text.Json<\/code> to be a viable choice for a JSON stack in more .NET applications, weighing each request against our design principles. See <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/43620\">dotnet\/runtime 43620<\/a> for an overview of what is being proposed. Here is a sneak peak of some of the top features.<\/p>\n<h3>C# source generator for JSON serialization (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/1568\">dotnet\/runtime #1568<\/a>)<\/h3>\n<p>One area of investment is utilizing the new <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/new-c-source-generator-samples\/\">C# source generators feature<\/a> to generate code that can help the serializer in the following areas:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Improved start up perf<\/li>\n<li>Improved run-time throughput<\/li>\n<li>Reduced private bytes usage<\/li>\n<li>ILLinker friendliness due to avoiding runtime reflection<\/li>\n<li>Reduced application size by facilitating linker removal of unused reflection-based code-paths of the serializer and unused converters<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>This effort is underway and is in a prototype stage. Action items and progress of this effort can be observed through the <a href=\"https:\/\/github.com\/dotnet\/runtimelab\/projects\/1\">JSON Code Gen project board<\/a> in <a href=\"https:\/\/github.com\/dotnet\/runtimelab\">dotnet\/runtimelab<\/a>.<\/p>\n<p>The updated <code>System.Text.Json<\/code> with support for source generation can be consumed via an experimental <a href=\"https:\/\/dev.azure.com\/dnceng\/public\/_packaging?_a=package&amp;feed=dotnet-experimental&amp;view=versions&amp;package=System.Text.Json&amp;protocolType=NuGet\">NuGet package<\/a>. Please share with us any performance changes you observe when using this feature. Issues can be logged <a href=\"https:\/\/github.com\/dotnet\/runtimelab\/issues\/new?labels=area-JsonCodeGen\">here with the <code>area-JsonCodeGen<\/code> label<\/a>.<\/p>\n<h3>Extended polymorphic serialization and deserialization (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45189\">dotnet\/runtime #45189<\/a>)<\/h3>\n<p>Polymorphic serialization and deserialization remain important scenarios for modern .NET applications. We expect feature work to happen in .NET 6.0 to enable these scenarios.<\/p>\n<h3><code>dynamic<\/code> and a mutable JSON DOM (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45188\">dotnet\/runtime #45188<\/a>)<\/h3>\n<p>Enabling serializing and deserializing <code>dynamic<\/code> types and providing a mutable JSON document are two closely related and important features that will unblock many customers. We are tracking these as potential work items for .NET 6.0.<\/p>\n<h3>Miscellaneous improvements (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/45190\">dotnet\/runtime #45190<\/a>)<\/h3>\n<p>There are multiple miscellaneous features and improvements that we would like to make to make in .NET 6.0. Some features would be innovative and unique to <code>System.Text.Json<\/code>, such as a feature to support asynchronously serializing and deserializing <code>IAsyncEnumerable&lt;T&gt;<\/code> instances (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/1570\">dotnet\/runtime #1570<\/a>). Other features would be more familiar, such as adding snake_case support (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/782\">dotnet\/runtime #782<\/a>), and being able to change the default <code>JsonSerializerOptions<\/code> settings (<a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/31094\">dotnet\/runtime #31094<\/a>). Implementing these features would increase the number of apps for which <code>System.Text.Json<\/code> is a good fit.<\/p>\n<h2>Closing<\/h2>\n<p>.NET 5.0 was a big release for <code>System.Text.Json<\/code>. If your project is not targeting .NET 5.0, you can still take advantage of the new improvements by installing the latest <a href=\"https:\/\/www.nuget.org\/packages\/System.Text.Json\/\"><code>System.Text.Json<\/code> NuGet package<\/a>.<\/p>\n<p>A lot of the work we did in .NET 5.0 was driven by the community. <a href=\"https:\/\/github.com\/YohDeadfall\">@YohDeadfall<\/a> implemented <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/36986\">field support<\/a> and contributed various <a href=\"https:\/\/github.com\/dotnet\/runtime\/pulls?q=is%3Apr+author%3AYohDeadfall+is%3Aclosed+label%3Aarea-System.Text.Json\">optimizations<\/a> to <code>JsonSerializer<\/code>. <a href=\"https:\/\/github.com\/NikiforovAll\">@NikiforovAll<\/a> implemented the <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/36073\"><code>JsonSerializerOptions<\/code> constructor that takes a <code>JsonSerializerDefaults<\/code> value<\/a>. <a href=\"https:\/\/github.com\/marcusturewicz\">@marcusturewicz<\/a> implemented <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/34537\">support for serializing and deserializing <code>JsonDocument<\/code> instances<\/a>. <a href=\"https:\/\/github.com\/devsko\">@devsko<\/a> contributed <a href=\"https:\/\/github.com\/dotnet\/runtime\/pulls?q=is%3Apr+is%3Aclosed+label%3Aarea-System.Text.Json+author%3Adevsko\">fixes<\/a> to various issues with new .NET 5.0 improvements before we shipped. <a href=\"https:\/\/github.com\/CodeBlanch\">@CodeBlanch<\/a> contributed <a href=\"https:\/\/github.com\/dotnet\/runtime\/pulls?q=is%3Apr+author%3ACodeBlanch+is%3Aclosed+label%3Aarea-System.Text.Json\">fixes<\/a> for issues with <code>null<\/code> and nullability. <a href=\"https:\/\/github.com\/Marusyk\">@Marusyk<\/a> contributed a <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/31756\">fix<\/a> for a bug with reference equality for the new preserve-references feature. <a href=\"https:\/\/github.com\/khellang\">@khellang<\/a> contributed a <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/478\">fix<\/a> for a bug with the validation of <code>DateTime<\/code> and <code>DateTimeOffset<\/code> payloads when reading. <a href=\"https:\/\/github.com\/alanisaac\">@alanisaac<\/a>, <a href=\"https:\/\/github.com\/thomaslevesque\">@thomaslevesque<\/a>, <a href=\"https:\/\/github.com\/marcusturewicz\">@marcusturewicz<\/a>, <a href=\"https:\/\/github.com\/madmir\">@madmir<\/a>, <a href=\"https:\/\/github.com\/NikiforovAll\">@NikiforovAll<\/a>, <a href=\"https:\/\/github.com\/JoshSchreuder\">@JoshSchreuder<\/a>, <a href=\"https:\/\/github.com\/Jacksondr5\">@Jacksondr5<\/a>, and <a href=\"https:\/\/github.com\/KimKiHyuk\">@KimKiHyuk<\/a> contributed changes to <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/32341\">improve test coverage for System.Text.Json<\/a> in the continuous effort to get it close to 100%. We can&#8217;t highlight all the contributions here, but the <a href=\"https:\/\/dotnetthanks.azurewebsites.net\/\">.NET Thanks<\/a> page gives kudos to all contributors to the .NET runtime.<\/p>\n<p>In .NET 6.0, we&#8217;re continuing to make more improvements. Contributions to <code>System.Text.Json<\/code> are very welcome as we make progress. Simply visit this <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues?q=is%3Aopen+is%3Aissue+label%3Aarea-System.Text.Json+label%3Aup-for-grabs\">GitHub query<\/a> to find issues labeled with <code>area-System.Text.Json<\/code> and <code>up-for-grabs<\/code>. If you are feeling bold, check out the issues without the <code>up-for-grabs<\/code> label. As always, feedback is very welcome, especially now as we are currently planning for the next release.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What&#8217;s next for System.Text.Json?<\/p>\n","protected":false},"author":39856,"featured_media":30464,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,196,3009],"tags":[7198,9,7223],"class_list":["post-31287","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-dotnet-core","category-performance","tag-net-5-0","tag-net-core","tag-system-text-json"],"acf":[],"blog_post_summary":"<p>What&#8217;s next for System.Text.Json?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/31287","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/39856"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=31287"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/31287\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/30464"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=31287"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=31287"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=31287"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}