Skip to content

Commit

Permalink
More System.Text.Json updates for .NET 9 (dotnet#43100)
Browse files Browse the repository at this point in the history
  • Loading branch information
gewarren authored Oct 17, 2024
1 parent b013edb commit 6d63bec
Show file tree
Hide file tree
Showing 24 changed files with 293 additions and 263 deletions.
5 changes: 5 additions & 0 deletions .openpublishing.redirection.standard.json
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,11 @@
"source_path_from_root": "/docs/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md",
"redirect_url": "/dotnet/standard/serialization/system-text-json/use-dom"
},
{
"source_path_from_root": "/docs/standard/serialization/system-text-json/json-schema-exporter.md",
"redirect_url": "/dotnet/standard/serialization/system-text-json/extract-schema",
"redirect_document_id": true
},
{
"source_path_from_root": "/docs/standard/serialization/system-text-json/use-dom-utf8jsonreader-utf8jsonwriter.md",
"redirect_url": "/dotnet/standard/serialization/system-text-json/use-dom",
Expand Down
73 changes: 3 additions & 70 deletions docs/core/whats-new/dotnet-9/libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,33 +493,7 @@ If you want to serialize with the [default options that ASP.NET Core uses](../..

JSON is frequently used to represent types in method signatures as part of remote procedure&ndash;calling schemes. It's used, for example, as part of OpenAPI specifications, or as part of tool calling with AI services like those from OpenAI. Developers can serialize and deserialize .NET types as JSON using <xref:System.Text.Json>. But they also need to be able to get a JSON schema that describes the shape of the .NET type (that is, describes the shape of what would be serialized and what can be deserialized). <xref:System.Text.Json> now provides the <xref:System.Text.Json.Schema.JsonSchemaExporter> type, which supports generating a JSON schema that represents a .NET type.

The following code generates a JSON schema from a type.

:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Schema":::

The type is defined as follows:

:::code language="csharp" source="../snippets/dotnet-9/csharp/Serialization.cs" id="Book":::

The generated schema is:

```json
{
"type": ["object", "null"],
"properties": {
"Title": {
"type": "string"
},
"Author": {
"type": ["string", "null"]
},
"PublishYear": {
"type": "integer"
}
},
"required": ["Title"]
}
```
For more information, see [JSON schema exporter](../../../standard/serialization/system-text-json/extract-schema.md).

### Respect nullable annotations

Expand Down Expand Up @@ -588,50 +562,9 @@ enum MyEnum

### Stream multiple JSON documents

<xref:System.Text.Json.Utf8JsonReader?displayProperty=nameWithType> now supports reading multiple, whitespace-separated JSON documents from a single buffer or stream. By default, the reader throws an exception if it detects any non-whitespace characters that are trailing the first top-level document. You can change this behavior using the <xref:System.Text.Json.JsonReaderOptions.AllowMultipleValues> flag:

```csharp
JsonReaderOptions options = new() { AllowMultipleValues = true };
Utf8JsonReader reader = new("null {} 1 \r\n [1,2,3]"u8, options);

reader.Read();
Console.WriteLine(reader.TokenType); // Null
reader.Read();
Console.WriteLine(reader.TokenType); // StartObject
reader.Skip();

reader.Read();
Console.WriteLine(reader.TokenType); // Number
<xref:System.Text.Json.Utf8JsonReader?displayProperty=nameWithType> now supports reading multiple, whitespace-separated JSON documents from a single buffer or stream. By default, the reader throws an exception if it detects any non-whitespace characters that are trailing the first top-level document. You can change this behavior using the <xref:System.Text.Json.JsonReaderOptions.AllowMultipleValues> flag.

reader.Read();
Console.WriteLine(reader.TokenType); // StartArray
reader.Skip();

Console.WriteLine(reader.Read()); // False
```

This flag also makes it possible to read JSON from payloads that might contain trailing data that's invalid JSON:

```csharp
Utf8JsonReader reader = new("[1,2,3] <NotJson/>"u8, new() { AllowMultipleValues = true });

reader.Read();
reader.Skip(); // Success
reader.Read(); // throws JsonReaderException
```

When it comes to streaming deserialization, a new <xref:System.Text.Json.JsonSerializer.DeserializeAsyncEnumerable%60%601(System.IO.Stream,System.Boolean,System.Text.Json.JsonSerializerOptions,System.Threading.CancellationToken)?displayProperty=nameWithType> overload makes streaming multiple top-level values possible. By default, the method attempts to stream elements that are contained in a top-level JSON array. You can toggle this behavior using the new `topLevelValues` flag:

```csharp
ReadOnlySpan<byte> utf8Json = """[0] [0,1] [0,1,1] [0,1,1,2] [0,1,1,2,3]"""u8;
using var stream = new MemoryStream(utf8Json.ToArray());

await foreach (int[] item in JsonSerializer.DeserializeAsyncEnumerable<int[]>(stream, topLevelValues: true))
{
Console.WriteLine(item.Length);
}
```
For more information, see [Read multiple JSON documents](../../../standard/serialization/system-text-json/use-utf8jsonreader.md#read-multiple-json-documents).

## Spans

Expand Down
2 changes: 1 addition & 1 deletion docs/fundamentals/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ items:
- name: Customize contracts
href: ../standard/serialization/system-text-json/custom-contracts.md
- name: Extract JSON schema
href: ../standard/serialization/system-text-json/json-schema-exporter.md
href: ../standard/serialization/system-text-json/extract-schema.md
- name: XML and SOAP serialization
items:
- name: Overview
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dev_langs:

# JSON schema exporter

The new <xref:System.Text.Json.Schema.JsonSchemaExporter> class lets you extract [JSON schema](https://json-schema.org/) documents from .NET types using either a <xref:System.Text.Json.JsonSerializerOptions> or <xref:System.Text.Json.Serialization.Metadata.JsonTypeInfo> instance. The resultant schema provides a specification of the JSON serialization contract for the type.
The <xref:System.Text.Json.Schema.JsonSchemaExporter> class, introduced in .NET 9, lets you extract [JSON schema](https://json-schema.org/) documents from .NET types using either a <xref:System.Text.Json.JsonSerializerOptions> or <xref:System.Text.Json.Serialization.Metadata.JsonTypeInfo> instance. The resultant schema provides a specification of the JSON serialization contract for the .NET type. The schema describes the shape of what would be serialized and what can be deserialized.

The following code snippet shows an example.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ helpviewer_keywords:
ms.topic: how-to
---

# How to handle overflow JSON or use JsonElement or JsonNode in System.Text.Json
# How to handle overflow JSON or use JsonElement or JsonNode

This article shows how to handle overflow JSON with the <xref:System.Text.Json> namespace. It also shows how to deserialize into <xref:System.Text.Json.JsonElement> or <xref:System.Text.Json.Nodes.JsonNode>, as an alternative for other scenarios where the target type might not perfectly match all of the JSON being deserialized.

Expand Down Expand Up @@ -86,7 +86,7 @@ The following example shows a round trip from JSON to a deserialized object and

## Deserialize into JsonElement or JsonNode

If you just want to be flexible about what JSON is acceptable for a particular property, an alternative is to deserialize into <xref:System.Text.Json.JsonElement> or <xref:System.Text.Json.Nodes.JsonNode>. Any valid JSON property can be deserialized into `JsonElement` or `JsonNode`. Choose `JsonElement` to create an immutable object or `JsonNode` to create a mutable object.
If you just want to be flexible about what JSON is acceptable for a particular property, an alternative is to deserialize into <xref:System.Text.Json.JsonElement> or <xref:System.Text.Json.Nodes.JsonNode>. Any valid JSON property can be deserialized into `JsonElement` or `JsonNode`. Choose `JsonElement` to create an *immutable* object or `JsonNode` to create a *mutable* object.

The following example shows a round trip from JSON and back to JSON for a class that includes properties of type `JsonElement` and `JsonNode`.

Expand Down
4 changes: 3 additions & 1 deletion docs/standard/serialization/system-text-json/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The following example creates JSON as a string:
:::code language="csharp" source="snippets/how-to/csharp/SerializeBasic.cs" id="all" highlight="23":::
:::code language="vb" source="snippets/how-to/vb/RoundtripToString.vb" id="Serialize":::

The JSON output is minified (whitespace, indentation, and new-line characters are removed) by default.
The JSON output is *minified* (whitespace, indentation, and new-line characters are removed) by default.

The following example uses synchronous code to create a JSON file:

Expand Down Expand Up @@ -104,6 +104,8 @@ To pretty-print the JSON output, set <xref:System.Text.Json.JsonSerializerOption
:::code language="csharp" source="snippets/how-to/csharp/SerializeWriteIndented.cs" highlight="24":::
:::code language="vb" source="snippets/how-to/vb/RoundtripToString.vb" id="SerializePrettyPrint":::

Starting in .NET 9, you can also customize the indent character and size using <xref:System.Text.Json.JsonSerializerOptions.IndentCharacter> and <xref:System.Text.Json.JsonSerializerOptions.IndentSize>.

> [!TIP]
> If you use `JsonSerializerOptions` repeatedly with the same options, don't create a new `JsonSerializerOptions` instance each time you use it. Reuse the same instance for every call. For more information, see [Reuse JsonSerializerOptions instances](configure-options.md#reuse-jsonserializeroptions-instances).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -782,8 +782,8 @@ If you need to continue to use `Newtonsoft.Json` for certain target frameworks,

Starting in .NET 9, you can customize the indentation character and size for <xref:System.Text.Json.Utf8JsonWriter> using options exposed by the <xref:System.Text.Json.JsonWriterOptions> struct:

* `JsonWriterOptions.IndentCharacter` <!-- <xref:System.Text.Json.JsonWriterOptions.IndentCharacter> -->
* `JsonWriterOptions.IndentSize` <!-- <xref:System.Text.Json.JsonWriterOptions.IndentSize> -->
* <xref:System.Text.Json.JsonWriterOptions.IndentCharacter?displayProperty=nameWithType>
* <xref:System.Text.Json.JsonWriterOptions.IndentSize?displayProperty=nameWithType>

::: zone-end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public struct Forecast
public DateTime Date { get; }
public int TemperatureC { get; }
public string Summary { get; }

[JsonConstructor]
public Forecast(DateTime date, int temperatureC, string summary) =>
(Date, TemperatureC, Summary) = (date, temperatureC, summary);
Expand All @@ -27,7 +27,7 @@ public static void Main()
""";
Console.WriteLine($"Input JSON: {json}");

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var options = JsonSerializerOptions.Web;

Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public readonly struct Forecast
[JsonPropertyName("celsius")]
public int TemperatureC { get; }
public string Summary { get; }

[JsonConstructor]
public Forecast(DateTime date, int temperatureC, string summary) =>
(Date, TemperatureC, Summary) = (date, temperatureC, summary);
Expand All @@ -28,7 +28,7 @@ public static void Main()
""";
Console.WriteLine($"Input JSON: {json}");

var options = new JsonSerializerOptions(JsonSerializerDefaults.Web);
var options = JsonSerializerOptions.Web;

Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<StartupObject>SystemTextJsonHowTo.Program</StartupObject>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ Namespace ImmutableTypes
Dim json As String = "{""date"":""2020-09-06T11:31:01.923395-07:00"",""temperatureC"":-1,""summary"":""Cold""}"
Console.WriteLine($"Input JSON: {json}")

Dim options As New JsonSerializerOptions(JsonSerializerDefaults.Web)

Dim forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, options)
Dim forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, JsonSerializerOptions.Web)

Console.WriteLine($"forecast.Date: {forecast1.[Date]}")
Console.WriteLine($"forecast.TemperatureC: {forecast1.TemperatureC}")
Console.WriteLine($"forecast.Summary: {forecast1.Summary}")

Dim roundTrippedJson As String = JsonSerializer.Serialize(forecast1, options)
Dim roundTrippedJson As String = JsonSerializer.Serialize(forecast1, JsonSerializerOptions.Web)

Console.WriteLine($"Output JSON: {roundTrippedJson}")
End Sub
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,49 @@
ImmutableTypes.Program.Main()
Console.WriteLine()

Console.WriteLine("======== Field support =========")
Fields.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Field support =========")
'Fields.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Non-string key dictionary =========")
NonStringKeyDictionary.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Non-string key dictionary =========")
'NonStringKeyDictionary.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== HttpClient extension methods =========")
Await HttpClientExtensionMethods.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== HttpClient extension methods =========")
'Await HttpClientExtensionMethods.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Ignore value type default on serialize =========")
IgnoreValueDefaultOnSerialize.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Ignore value type default on serialize =========")
'IgnoreValueDefaultOnSerialize.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Ignore null on serialize =========")
IgnoreNullOnSerialize.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Ignore null on serialize =========")
'IgnoreNullOnSerialize.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Conditionally ignore selected properties on serialize =========")
JsonIgnoreAttributeExample.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Conditionally ignore selected properties on serialize =========")
'JsonIgnoreAttributeExample.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Non-public accessors =========")
NonPublicAccessors.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Non-public accessors =========")
'NonPublicAccessors.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Copy options instance =========")
CopyOptions.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Copy options instance =========")
'CopyOptions.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Create options instance with specified defaults =========")
OptionsDefaults.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Create options instance with specified defaults =========")
'OptionsDefaults.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== Quoted numbers =========")
QuotedNumbers.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== Quoted numbers =========")
'QuotedNumbers.Program.Main()
'Console.WriteLine()

Console.WriteLine("======== GuidReferenceResolver =========")
GuidReferenceResolverExample.Program.Main()
Console.WriteLine()
'Console.WriteLine("======== GuidReferenceResolver =========")
'GuidReferenceResolverExample.Program.Main()
'Console.WriteLine()
End Function

End Module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<StartupObject>SystemTextJsonHowTo.SystemTextJsonHowTo.Program</StartupObject>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ public static void Run()

AverageGrades(jsonString);
AverageGrades_Alternative(jsonString);

Compare();
}

private static void AverageGrades(string jsonString)
{
// <AverageGrades1>
Expand Down Expand Up @@ -40,6 +43,7 @@ private static void AverageGrades(string jsonString)
Console.WriteLine($"Average grade : {average}");
// </AverageGrades1>
}

private static void AverageGrades_Alternative(string jsonString)
{
// <AverageGrades2>
Expand Down Expand Up @@ -70,5 +74,15 @@ private static void AverageGrades_Alternative(string jsonString)
Console.WriteLine($"Average grade : {average}");
// </AverageGrades2>
}

private static void Compare()
{
// <DeepEquals>
JsonElement left = JsonDocument.Parse("10e-3").RootElement;
JsonElement right = JsonDocument.Parse("0.01").RootElement;
bool equal = JsonElement.DeepEquals(left, right);
Console.WriteLine(equal); // True.
// </DeepEquals>
}
}
}
Loading

0 comments on commit 6d63bec

Please sign in to comment.