{"id":1333,"date":"2024-10-15T16:22:19","date_gmt":"2024-10-15T16:22:19","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/10\/15\/whats-new-in-system-text-json-in-net-9\/"},"modified":"2024-10-15T16:22:19","modified_gmt":"2024-10-15T16:22:19","slug":"whats-new-in-system-text-json-in-net-9","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/10\/15\/whats-new-in-system-text-json-in-net-9\/","title":{"rendered":"What\u2019s new in System.Text.Json in .NET 9"},"content":{"rendered":"<p>The 9.0 release of System.Text.Json includes many features, primarily with a focus on JSON schema and intelligent application support. It also includes highly requested enhancements such as nullable reference type support, customizing enum member names, out-of-order metadata deserialization and customizing serialization indentation.<\/p>\n<h3>Getting the latest bits<\/h3>\n<p>You can try out the new features by referencing the latest build of <a href=\"https:\/\/www.nuget.org\/packages\/System.Text.Json\">System.Text.Json NuGet package<\/a> or the <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/9.0\">latest SDK for .NET 9<\/a>.<\/p>\n<h2>JSON Schema Exporter<\/h2>\n<p>The new JsonSchemaExporter class can be used to extract <a href=\"https:\/\/json-schema.org\/\">JSON schema<\/a> documents from .NET types using either JsonSerializerOptions or JsonTypeInfo instances:<\/p>\n<p>using System.Text.Json.Schema;<\/p>\n<p>JsonSerializerOptions options = JsonSerializerOptions.Default;<br \/>\nJsonNode schema = options.GetJsonSchemaAsNode(typeof(Person));<br \/>\nConsole.WriteLine(schema.ToString());<br \/>\n\/\/{<br \/>\n\/\/  &#8220;type&#8221;: [&#8220;object&#8221;, &#8220;null&#8221;],<br \/>\n\/\/  &#8220;properties&#8221;: {<br \/>\n\/\/    &#8220;Name&#8221;: { &#8220;type&#8221;: &#8220;string&#8221; },<br \/>\n\/\/    &#8220;Age&#8221;: { &#8220;type&#8221;: &#8220;integer&#8221; },<br \/>\n\/\/    &#8220;Address&#8221;: { &#8220;type&#8221;: [&#8220;string&#8221;, &#8220;null&#8221;], &#8220;default&#8221;: null }<br \/>\n\/\/  },<br \/>\n\/\/  &#8220;required&#8221;: [&#8220;Name&#8221;, &#8220;Age&#8221;]<br \/>\n\/\/}<\/p>\n<p>record Person(string Name, int Age, string? Address = null);<\/p>\n<p>The resultant schema provides a specification of the JSON serialization contract for the type. As can be seen in this example, it distinguishes between nullable and non-nullable properties and populates the required keyword by virtue of a constructor parameter being optional or not. The schema output can be influenced by configuration specified in the JsonSerializerOptions or JsonTypeInfo instances:<\/p>\n<p>JsonSerializerOptions options = new(JsonSerializerOptions.Default)<br \/>\n{<br \/>\n    PropertyNamingPolicy = JsonNamingPolicy.KebabCaseUpper,<br \/>\n    NumberHandling = JsonNumberHandling.WriteAsString,<br \/>\n    UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,<br \/>\n};<\/p>\n<p>JsonNode schema = options.GetJsonSchemaAsNode(typeof(MyPoco));<br \/>\nConsole.WriteLine(schema.ToString());<br \/>\n\/\/{<br \/>\n\/\/  &#8220;type&#8221;: [&#8220;object&#8221;, &#8220;null&#8221;],<br \/>\n\/\/  &#8220;properties&#8221;: {<br \/>\n\/\/    &#8220;NUMERIC-VALUE&#8221;: {<br \/>\n\/\/      &#8220;type&#8221;: [&#8220;string&#8221;, &#8220;integer&#8221;],<br \/>\n\/\/      &#8220;pattern&#8221;: &#8220;^-?(?:0|[1-9]\\d*)$&#8221;<br \/>\n\/\/    }<br \/>\n\/\/  },<br \/>\n\/\/  &#8220;additionalProperties&#8221;: false<br \/>\n\/\/}<\/p>\n<p>class MyPoco<br \/>\n{<br \/>\n    public int NumericValue { get; init; }<br \/>\n}<\/p>\n<p>The generated schema can be further controlled using the JsonSchemaExporterOptions configuration type:<\/p>\n<p>JsonSerializerOptions options = JsonSerializerOptions.Default;<br \/>\nJsonSchemaExporterOptions exporterOptions = new()<br \/>\n{<br \/>\n    \/\/ Marks root-level types as non-nullable<br \/>\n    TreatNullObliviousAsNonNullable = true,<br \/>\n};<\/p>\n<p>JsonNode schema = options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);<br \/>\nConsole.WriteLine(schema.ToString());<br \/>\n\/\/{<br \/>\n\/\/  &#8220;type&#8221;: &#8220;object&#8221;,<br \/>\n\/\/  &#8220;properties&#8221;: {<br \/>\n\/\/    &#8220;Name&#8221;: { &#8220;type&#8221;: &#8220;string&#8221; }<br \/>\n\/\/  },<br \/>\n\/\/  &#8220;required&#8221;: [&#8220;Name&#8221;]<br \/>\n\/\/}<\/p>\n<p>record Person(string Name);<\/p>\n<p>Finally, users are able to apply their own transformations to generated schema nodes by specifying a TransformSchemaNode delegate. Here\u2019s an example that incorporates text from DescriptionAttribute annotations:<\/p>\n<p>JsonSchemaExporterOptions exporterOptions = new()<br \/>\n{<br \/>\n    TransformSchemaNode = (context, schema) =&gt;<br \/>\n    {<br \/>\n        \/\/ Determine if a type or property and extract the relevant attribute provider<br \/>\n        ICustomAttributeProvider? attributeProvider = context.PropertyInfo is not null<br \/>\n            ? context.PropertyInfo.AttributeProvider<br \/>\n            : context.TypeInfo.Type;<\/p>\n<p>        \/\/ Look up any description attributes<br \/>\n        DescriptionAttribute? descriptionAttr = attributeProvider?<br \/>\n            .GetCustomAttributes(inherit: true)<br \/>\n            .Select(attr =&gt; attr as DescriptionAttribute)<br \/>\n            .FirstOrDefault(attr =&gt; attr is not null);<\/p>\n<p>        \/\/ Apply description attribute to the generated schema<br \/>\n        if (descriptionAttr != null)<br \/>\n        {<br \/>\n            if (schema is not JsonObject jObj)<br \/>\n            {<br \/>\n                \/\/ Handle the case where the schema is a boolean<br \/>\n                JsonValueKind valueKind = schema.GetValueKind();<br \/>\n                Debug.Assert(valueKind is JsonValueKind.True or JsonValueKind.False);<br \/>\n                schema = jObj = new JsonObject();<br \/>\n                if (valueKind is JsonValueKind.False)<br \/>\n                {<br \/>\n                    jObj.Add(&#8220;not&#8221;, true);<br \/>\n                }<br \/>\n            }<\/p>\n<p>            jObj.Insert(0, &#8220;description&#8221;, descriptionAttr.Description);<br \/>\n        }<\/p>\n<p>        return schema;<br \/>\n    }<br \/>\n};<\/p>\n<p>Tying it all together, we can now generate a schema that incorporates description keyword source from attribute annotations:<\/p>\n<p>JsonNode schema = options.GetJsonSchemaAsNode(typeof(Person), exporterOptions);<br \/>\nConsole.WriteLine(schema.ToString());<br \/>\n\/\/{<br \/>\n\/\/  &#8220;description&#8221;: &#8220;A person&#8221;,<br \/>\n\/\/  &#8220;type&#8221;: [&#8220;object&#8221;, &#8220;null&#8221;],<br \/>\n\/\/  &#8220;properties&#8221;: {<br \/>\n\/\/    &#8220;Name&#8221;: { &#8220;description&#8221;: &#8220;The name of the person&#8221;, &#8220;type&#8221;: &#8220;string&#8221; }<br \/>\n\/\/  },<br \/>\n\/\/  &#8220;required&#8221;: [&#8220;Name&#8221;]<br \/>\n\/\/}<\/p>\n<p>[Description(&#8220;A person&#8221;)]<br \/>\nrecord Person([property: Description(&#8220;The name of the person&#8221;)] string Name);<\/p>\n<p>This is a particularly useful component when it comes to generating schemas for .NET methods or APIs; it is being used to power ASP.NET Core\u2019s newly released OpenAPI component and we\u2019ve deployed it to a number of AI related libraries and applications with tool calling requirements such as Semantic Kernel, Visual Studio Copilot and the the newly released Microsoft.Extensions.AI library.<\/p>\n<h2>Streaming multiple JSON documents<\/h2>\n<p>Utf8JsonReader now supports reading multiple, whitespace-separated JSON documents from a single buffer or stream. By default, Utf8JsonReader will throw an exception if it detects any non-whitespace characters that are trailing the first top-level document. This behavior can be changed using the JsonReaderOptions.AllowMultipleValues flag:<\/p>\n<p>JsonReaderOptions options = new() { AllowMultipleValues = true };<br \/>\nUtf8JsonReader reader = new(&#8220;null {} 1 rn [1,2,3]&#8221;u8, options);<\/p>\n<p>reader.Read();<br \/>\nConsole.WriteLine(reader.TokenType); \/\/ Null<\/p>\n<p>reader.Read();<br \/>\nConsole.WriteLine(reader.TokenType); \/\/ StartObject<br \/>\nreader.Skip();<\/p>\n<p>reader.Read();<br \/>\nConsole.WriteLine(reader.TokenType); \/\/ Number<\/p>\n<p>reader.Read();<br \/>\nConsole.WriteLine(reader.TokenType); \/\/ StartArray<br \/>\nreader.Skip();<\/p>\n<p>Console.WriteLine(reader.Read()); \/\/ False<\/p>\n<p>This additionally makes it possible to read JSON from payloads that may contain trailing data that is invalid JSON:<\/p>\n<p>Utf8JsonReader reader = new(&#8220;[1,2,3]    &lt;NotJson\/&gt;&#8221;u8, new() { AllowMultipleValues = true });<\/p>\n<p>reader.Read();<br \/>\nreader.Skip(); \/\/ Success<br \/>\nreader.Read(); \/\/ throws JsonReaderException<\/p>\n<p>When it comes to streaming deserialization, we have included a new JsonSerializer.DeserializeAsyncEnumerable overload that makes streaming multiple top-level values possible. By default, DeserializeAsyncEnumerable will attempt to stream elements that are contained in a top-level JSON array. This behavior can be toggled using the new topLevelValues flag:<\/p>\n<p>ReadOnlySpan&lt;byte&gt; utf8Json = &#8220;&#8221;&#8221;[0] [0,1] [0,1,1] [0,1,1,2] [0,1,1,2,3]&#8221;&#8221;&#8221;u8;<br \/>\nusing var stream = new MemoryStream(utf8Json.ToArray());<\/p>\n<p>await foreach (int[] item in JsonSerializer.DeserializeAsyncEnumerable&lt;int[]&gt;(stream, topLevelValues: true))<br \/>\n{<br \/>\n    Console.WriteLine(item.Length);<br \/>\n}<\/p>\n<h2>Respecting nullable annotations<\/h2>\n<p>JsonSerializer now adds limited support for non-nullable reference type enforcement in serialization and deserialization. This can be toggled using the RespectNullableAnnotations flag:<\/p>\n<p>#nullable enable<br \/>\nJsonSerializerOptions options = new() { RespectNullableAnnotations = true };<\/p>\n<p>MyPoco invalidValue = new(Name: null!);<br \/>\nJsonSerializer.Serialize(invalidValue, options);<br \/>\n\/\/ System.Text.Json.JsonException: The property or field &#8216;Name&#8217;<br \/>\n\/\/ on type &#8216;MyPoco&#8217; doesn&#8217;t allow getting null values. Consider<br \/>\n\/\/ updating its nullability annotation. <\/p>\n<p>record MyPoco(string Name);<\/p>\n<p>Similarly, this setting adds enforcement on deserialization:<\/p>\n<p>JsonSerializerOptions options = new() { RespectNullableAnnotations = true };<br \/>\nstring json = &#8220;&#8221;&#8221;{&#8220;Name&#8221;:null}&#8221;&#8221;&#8221;;<br \/>\nJsonSerializer.Deserialize&lt;MyPoco&gt;(json, options);<br \/>\n\/\/ System.Text.Json.JsonException: The constructor parameter &#8216;Name&#8217;<br \/>\n\/\/ on type &#8216;MyPoco&#8217; doesn&#8217;t allow null values. Consider updating<br \/>\n\/\/ its nullability annotation.<\/p>\n<h3>Limitations<\/h3>\n<p>Due to how non-nullable reference types are implemented, this feature comes with an important number of limitations that users need to familiarize themselves with before turning it on. The root of the issue is that reference type nullability has no first-class representation in IL, as such the expressions MyPoco and MyPoco? <a href=\"https:\/\/sharplab.io\/#v2:D4AQTAjAsAULAqBPADgUwAQBcLoLzvlQGdMAeAWUQAUB7AYxoD4AKASgG4EUNMw8DiZSrQYB+Fh1iwQEAJzNsefL0kwZANgLcBJUvAl5GWbjQBmzeKul9h9GuyA=\">are indistinguishable from the perspective of run-time reflection<\/a>. While the compiler will try to make up for that by <a href=\"https:\/\/sharplab.io\/#v2:D4AQTAjAsAULBOBTAxge3gEwAQFkCeACqmgBQgQAMWAcqgC7UCuANswMp3wCWAdgOYAaLOQoB+Gi2YBDAEbNEHbvwCUAbiA=\">emitting attribute metadata where possible<\/a>, this is restricted to non-generic member annotations that are scoped to a particular type definition. It is for this reason that the flag only validates nullability annotations that are present on non-generic properties, fields, and constructor parameters. System.Text.Json does not support nullability enforcement on<\/p>\n<p>Top-level types, aka the type that is passed when making the first JsonSerializer.(De)serialize call.<br \/>\nCollection element types, aka we cannot distinguish between List&lt;string&gt; and List&lt;string?&gt; types.<br \/>\nAny properties, fields, or constructor parameters that are generic.<\/p>\n<p>If you are looking to add nullability enforcement in these cases, we recommend that you either model your type to be a struct (since they do not admit null values) or author a custom converter that overrides its <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/system.text.json.serialization.jsonconverter-1.handlenull\">HandleNull<\/a> property to true.<\/p>\n<h3>Feature switch<\/h3>\n<p>Users can turn on the RespectNullableAnnotations setting globally using the System.Text.Json.Serialization.RespectNullableAnnotationsDefault feature switch, which can be set via your project configuration:<\/p>\n<p>&lt;ItemGroup&gt;<br \/>\n  &lt;RuntimeHostConfigurationOption Include=&#8221;System.Text.Json.Serialization.RespectNullableAnnotationsDefault&#8221; Value=&#8221;true&#8221; \/&gt;<br \/>\n&lt;\/ItemGroup&gt;<\/p>\n<h3>On the relation between nullable and optional parameters<\/h3>\n<p>It should be noted that RespectNullableAnnotations does not extend enforcement to unspecified JSON values:<\/p>\n<p>JsonSerializerOptions options = new() { RespectNullableAnnotations = true };<br \/>\nvar result = JsonSerializer.Deserialize&lt;MyPoco&gt;(&#8220;{}&#8221;, options); \/\/ No exception!<br \/>\nConsole.WriteLine(result.Name is null); \/\/ True<\/p>\n<p>class MyPoco<br \/>\n{<br \/>\n    public string Name { get; set; }<br \/>\n}<\/p>\n<p>This is because STJ treats required and non-nullable properties as orthogonal concepts. This stems from the C# language itself where you can have required properties that are nullable:<\/p>\n<p>MyPoco poco = new() { Value = null }; \/\/ No compiler warnings<\/p>\n<p>class MyPoco<br \/>\n{<br \/>\n    public required string? Value { get; set; }<br \/>\n}<\/p>\n<p>And optional properties that are non-nullable:<\/p>\n<p>class MyPoco<br \/>\n{<br \/>\n    public string Value { get; set; } = &#8220;default&#8221;;<br \/>\n}<\/p>\n<p>The same orthogonality applies to constructor parameters:<\/p>\n<p>record MyPoco(<br \/>\n    string RequiredNonNullable,<br \/>\n    string? RequiredNullable,<br \/>\n    string OptionalNonNullable = &#8220;default&#8221;,<br \/>\n    string? OptionalNullable = &#8220;default&#8221;);<\/p>\n<h2>Respecting non-optional constructor parameters<\/h2>\n<p>STJ constructor-based deserialization has historically been treating all constructor parameters as optional, as can be highlighted in the following example:<\/p>\n<p>var result = JsonSerializer.Deserialize&lt;Person&gt;(&#8220;{}&#8221;);<br \/>\nConsole.WriteLine(result); \/\/ Person { Name = , Age = 0 }<\/p>\n<p>record Person(string Name, int Age);<\/p>\n<p>In .NET 9 we\u2019re including the RespectRequiredConstructorParameters flag that changes the behavior such that non-optional constructor parameters are now treated as required:<\/p>\n<p>JsonSerializerOptions options = new() { RespectRequiredConstructorParameters = true };<br \/>\nstring json = &#8220;&#8221;&#8221;{&#8220;Optional&#8221;: &#8220;value&#8221;}&#8221;&#8221;&#8221;;<br \/>\nJsonSerializer.Deserialize&lt;MyPoco&gt;(json, options);<\/p>\n<p>record MyPoco(string Required, string? Optional = null);<br \/>\n\/\/ JsonException: JSON deserialization for type &#8216;MyPoco&#8217;<br \/>\n\/\/ was missing required properties including: &#8216;Required&#8217;.<\/p>\n<h3>Feature switch<\/h3>\n<p>Users can turn on the RespectRequiredConstructorParameters setting globally using the System.Text.Json.Serialization.RespectRequiredConstructorParametersDefault feature switch, which can be set via your project configuration:<\/p>\n<p>&lt;ItemGroup&gt;<br \/>\n  &lt;RuntimeHostConfigurationOption Include=&#8221;System.Text.Json.Serialization.RespectRequiredConstructorParametersDefault&#8221; Value=&#8221;true&#8221; \/&gt;<br \/>\n&lt;\/ItemGroup&gt;<\/p>\n<p>Both the RespectNullableAnnotations and RespectRequiredConstructorParameter properties were implemented as opt-in flags to avoid breaking existing applications. If you\u2019re writing a new application, it is highly recommended that you enable both flags in your code.<\/p>\n<h2>Customizing enum member names<\/h2>\n<p>The new JsonStringEnumMemberName attribute can be used to customize the names of individual enum members for types that are serialized as strings:<\/p>\n<p>JsonSerializer.Serialize(MyEnum.Value1 | MyEnum.Value2); \/\/ &#8220;Value1, Custom enum value&#8221;<\/p>\n<p>[Flags, JsonConverter(typeof(JsonStringEnumConverter))]<br \/>\nenum MyEnum<br \/>\n{<br \/>\n    Value1 = 1,<br \/>\n    [JsonStringEnumMemberName(&#8220;Custom enum value&#8221;)]<br \/>\n    Value2 = 2,<br \/>\n}<\/p>\n<h2>Out-of-order metadata reads<\/h2>\n<p>Certain features of System.Text.Json such as polymorphism or ReferenceHandler.Preserve require emitting metadata properties over the wire:<\/p>\n<p>JsonSerializerOptions options = new() { ReferenceHandler = ReferenceHandler.Preserve };<br \/>\nBase value = new Derived(&#8220;Name&#8221;);<br \/>\nJsonSerializer.Serialize(value, options); \/\/ {&#8220;$id&#8221;:&#8221;1&#8243;,&#8221;$type&#8221;:&#8221;derived&#8221;,&#8221;Name&#8221;:&#8221;Name&#8221;}<\/p>\n<p>[JsonDerivedType(typeof(Derived), &#8220;derived&#8221;)]<br \/>\nrecord Base;<br \/>\nrecord Derived(string Name) : Base;<\/p>\n<p>By default, the STJ metadata reader requires that the metadata properties $id and $type must be defined at the start of the JSON object:<\/p>\n<p>JsonSerializer.Deserialize&lt;Base&gt;(&#8220;&#8221;&#8221;{&#8220;Name&#8221;:&#8221;Name&#8221;,&#8221;$type&#8221;:&#8221;derived&#8221;}&#8221;&#8221;&#8221;);<br \/>\n\/\/ JsonException: The metadata property is either not supported by the<br \/>\n\/\/ type or is not the first property in the deserialized JSON object.<\/p>\n<p>This is known to create problems when needing to deserialize JSON payloads that do not originate from System.Text.Json. The new AllowOutOfOrderMetadataProperties can be configured to disable this restriction:<\/p>\n<p>JsonSerializerOptions options = new() { AllowOutOfOrderMetadataProperties = true };<br \/>\nJsonSerializer.Deserialize&lt;Base&gt;(&#8220;&#8221;&#8221;{&#8220;Name&#8221;:&#8221;Name&#8221;,&#8221;$type&#8221;:&#8221;derived&#8221;}&#8221;&#8221;&#8221;, options); \/\/ Success<\/p>\n<p>Care should be taken when enabling this flag, as it might result in over-buffering (and OOM failures) when performing streaming deserialization of very large JSON objects. This is because metadata properties must be read <em>before<\/em> the deserialize object is instantiated, meaning that all properties preceding a $type property must be kept in the buffer for subsequent property binding.<\/p>\n<h2>Customizing indentation<\/h2>\n<p>The JsonWriterOptions and JsonSerializerOptions types now expose APIs for configuring indentation. The following example enables single-tab indentation:<\/p>\n<p>JsonSerializerOptions options = new()<br \/>\n{<br \/>\n    WriteIndented = true,<br \/>\n    IndentCharacter = &#8216;t&#8217;,<br \/>\n    IndentSize = 1,<br \/>\n};<\/p>\n<p>JsonSerializer.Serialize(new { Value = 42 }, options);<\/p>\n<h2>JsonObject property order manipulation<\/h2>\n<p>The JsonObject type is part of the mutable DOM and is used to represent JSON objects. Even though the type is modelled as an IDictionary&lt;string, JsonNode&gt;, it does encapsulate an implicit property order that is not user-modifiable. The new release exposes additional APIs that effectively model the type as an ordered dictionary:<\/p>\n<p>public partial class JsonObject : IList&lt;KeyValuePair&lt;string, JsonNode?&gt;&gt;<br \/>\n{<br \/>\n    public int IndexOf(string key);<br \/>\n    public void Insert(int index, string key, JsonNode? value);<br \/>\n    public void RemoveAt(int index);<br \/>\n}<\/p>\n<p>This allows modifications to object instances that can directly influence property order:<\/p>\n<p>\/\/ Adds or moves the $id property to the start of the object<br \/>\nvar schema = (JsonObject)JsonSerializerOptions.Default.GetJsonSchemaAsNode(typeof(MyPoco));<br \/>\nswitch (schema.IndexOf(&#8220;$id&#8221;, out JsonNode? idValue))<br \/>\n{<br \/>\n    case &lt; 0: \/\/ $id property missing<br \/>\n       idValue = (JsonNode)&#8221;https:\/\/example.com\/schema&#8221;;<br \/>\n       schema.Insert(0, &#8220;$id&#8221;, idValue);<br \/>\n       break;<\/p>\n<p>    case 0: \/\/ $id property already at the start of the object<br \/>\n        break; <\/p>\n<p>    case int index: \/\/ $id exists but not at the start of the object<br \/>\n        schema.RemoveAt(index);<br \/>\n        schema.Insert(0, &#8220;$id&#8221;, idValue);<br \/>\n}<\/p>\n<h2>DeepEquals methods in JsonElement and JsonNode<\/h2>\n<p>The new JsonElement.DeepEquals method extends deep equality comparison to JsonElement instances, complementing the pre-existing JsonNode.DeepEquals method. Additionally, both methods include refinements in their implementation, such as handling of equivalent JSON numeric representations:<\/p>\n<p>JsonElement left = JsonDocument.Parse(&#8220;10e-3&#8221;).RootElement;<br \/>\nJsonElement right = JsonDocument.Parse(&#8220;0.01&#8221;).RootElement;<br \/>\nJsonElement.DeepEquals(left, right); \/\/ True<\/p>\n<h2>JsonSerializerOptions.Web<\/h2>\n<p>The new JsonSerializerOptions.Web singleton can be used to quickly serialize values using JsonSerializerDefaults.Web settings:<\/p>\n<p>JsonSerializerOptions options = JsonSerializerOptions.Web; \/\/ used instead of new(JsonSerializerDefaults.Web);<br \/>\nJsonSerializer.Serialize(new { Value = 42 }, options); \/\/ {&#8220;value&#8221;:42}<\/p>\n<h2>Performance improvements<\/h2>\n<p>For a detailed write-up of System.Text.Json performance improvements in .NET 9, please refer to the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-9\/#json\">relevant section<\/a> in Stephen Toub\u2019s \u201cPerformance Improvements in .NET 9\u201d article.<\/p>\n<h2>Closing<\/h2>\n<p>.NET 9 sees a large number of new features and quality of life improvements with a focus on JSON schema and intelligent application support. In total <a href=\"https:\/\/github.com\/dotnet\/runtime\/pulls?q=is:pr+is:merged+label:area-System.Text.Json+milestone:9.0.0\">46 pull requests<\/a> contributed to System.Text.Json during .NET 9 development. We\u2019d like you to try the new features and give us feedback on how it improves your applications, and any usability issues or bugs that you might encounter.<\/p>\n<p>Community contributions are always welcome. If you\u2019d like to contribute to System.Text.Json, check out our list of <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3Aarea-System.Text.Json\">help wanted issues<\/a> on GitHub.<\/p>\n<p>The post <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/system-text-json-in-dotnet-9\/\">What\u2019s new in System.Text.Json in .NET 9<\/a> appeared first on <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\">.NET Blog<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>The 9.0 release of System.Text.Json includes many features, primarily with a focus on JSON schema and intelligent application support. It [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[7],"tags":[],"class_list":["post-1333","post","type-post","status-publish","format-standard","hentry","category-dotnet"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/1333","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=1333"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/1333\/revisions"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=1333"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=1333"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=1333"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}