diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeAverageGradeExample.cs b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeAverageGradeExample.cs new file mode 100644 index 0000000000000..32c66f6296018 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeAverageGradeExample.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace JsonNodeAverageGradeExample +{ + public class Program + { + public static DateTime[] DatesAvailable { get; set; } + + public static void Main() + { + string jsonString = + @"{ + ""Class Name"": ""Science"", + ""Teacher\u0027s Name"": ""Jane"", + ""Semester"": ""2019-01-01"", + ""Students"": [ + { + ""Name"": ""John"", + ""Grade"": 94.3 + }, + { + ""Name"": ""James"", + ""Grade"": 81.0 + }, + { + ""Name"": ""Julia"", + ""Grade"": 91.9 + }, + { + ""Name"": ""Jessica"", + ""Grade"": 72.4 + }, + { + ""Name"": ""Johnathan"" + } + ], + ""Final"": true +} +"; + double sum = 0; + int count = 0; + + JsonNode document = JsonNode.Parse(jsonString); + + JsonNode root = document.Root; + JsonArray studentsArray = root["Students"].AsArray(); + + count = studentsArray.Count; + + foreach (JsonNode student in studentsArray) + { + if (student["Grade"] is JsonNode gradeNode) + { + sum += (double)gradeNode; + } + else + { + sum += 70; + } + } + + double average = sum / count; + Console.WriteLine($"Average grade : {average}"); + } + } +} +// output: +//Average grade : 81.92 diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromObjectExample.cs b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromObjectExample.cs new file mode 100644 index 0000000000000..c5f062353edbf --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromObjectExample.cs @@ -0,0 +1,66 @@ +using System; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace JsonNodeFromObjectExample +{ + public class Program + { + public static void Main() + { + // Create a new JsonObject using object initializers. + var forecastObject = new JsonObject + { + ["Date"] = new DateTime(2019, 8, 1), + ["Temperature"] = 25, + ["Summary"] = "Hot", + ["DatesAvailable"] = new JsonArray( + new DateTime(2019, 8, 1), new DateTime(2019, 8, 2)), + ["TemperatureRanges"] = new JsonObject + { + ["Cold"] = new JsonObject + { + ["High"] = 20, + ["Low"] = -10 + } + }, + ["SummaryWords"] = new JsonArray("Cool", "Windy", "Humid") + }; + + // Add an object. + forecastObject["TemperatureRanges"].AsObject().Add( + "Hot", new JsonObject { ["High"] = 60, ["Low"] = 20 }); + + // Remove a property. + forecastObject.Remove("SummaryWords"); + + // Change the value of a property. + forecastObject["Date"] = new DateTime(2019, 8, 3); + + var options = new JsonSerializerOptions { WriteIndented = true }; + Console.WriteLine(forecastObject.ToJsonString(options)); + //output: + //{ + // "Date": "2019-08-03T00:00:00", + // "Temperature": 25, + // "Summary": "Hot", + // "DatesAvailable": [ + // "2019-08-01T00:00:00", + // "2019-08-02T00:00:00" + // ], + // "TemperatureRanges": { + // "Cold": { + // "High": 20, + // "Low": -10 + // }, + // "Hot": { + // "High": 60, + // "Low": 20 + // } + // } + //} + } + } +} + + diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromStringExample.cs b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromStringExample.cs new file mode 100644 index 0000000000000..2ab78ffa1b3af --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromStringExample.cs @@ -0,0 +1,117 @@ +using System; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace JsonNodeFromStringExample +{ + public class Program + { + public static void Main() + { + string jsonString = +@"{ + ""Date"": ""2019-08-01T00:00:00"", + ""Temperature"": 25, + ""Summary"": ""Hot"", + ""DatesAvailable"": [ + ""2019-08-01T00:00:00"", + ""2019-08-02T00:00:00"" + ], + ""TemperatureRanges"": { + ""Cold"": { + ""High"": 20, + ""Low"": -10 + }, + ""Hot"": { + ""High"": 60, + ""Low"": 20 + } + } +} +"; + // Create a JsonNode DOM from a JSON string. + JsonNode forecastNode = JsonNode.Parse(jsonString); + + // Write JSON from a JsonNode + var options = new JsonSerializerOptions { WriteIndented = true }; + Console.WriteLine(forecastNode.ToJsonString(options)); + // output: + //{ + // "Date": "2019-08-01T00:00:00", + // "Temperature": 25, + // "Summary": "Hot", + // "DatesAvailable": [ + // "2019-08-01T00:00:00", + // "2019-08-02T00:00:00" + // ], + // "TemperatureRanges": { + // "Cold": { + // "High": 20, + // "Low": -10 + // }, + // "Hot": { + // "High": 60, + // "Low": 20 + // } + // } + //} + + // Get value from a JsonNode. + JsonNode temperatureNode = forecastNode["Temperature"]; + Console.WriteLine($"Type={temperatureNode.GetType()}"); + Console.WriteLine($"JSON={temperatureNode.ToJsonString()}"); + //output: + //Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement] + //JSON = 25 + + // Get a typed value from a JsonNode. + int temperatureInt = (int)forecastNode["Temperature"]; + Console.WriteLine($"Value={temperatureInt}"); + //output: + //Value=25 + + // Get a typed value from a JsonNode by using GetValue. + temperatureInt = forecastNode["Temperature"].GetValue(); + Console.WriteLine($"TemperatureInt={temperatureInt}"); + //output: + //Value=25 + + // Get a JSON object from a JsonNode. + JsonNode temperatureRanges = forecastNode["TemperatureRanges"]; + Console.WriteLine($"Type={temperatureRanges.GetType()}"); + Console.WriteLine($"JSON={temperatureRanges.ToJsonString()}"); + //output: + //Type = System.Text.Json.Nodes.JsonObject + //JSON = { "Cold":{ "High":20,"Low":-10},"Hot":{ "High":60,"Low":20} } + + // Get a JSON array from a JsonNode. + JsonNode datesAvailable = forecastNode["DatesAvailable"]; + Console.WriteLine($"Type={datesAvailable.GetType()}"); + Console.WriteLine($"JSON={datesAvailable.ToJsonString()}"); + //output: + //datesAvailable Type = System.Text.Json.Nodes.JsonArray + //datesAvailable JSON =["2019-08-01T00:00:00", "2019-08-02T00:00:00"] + + // Get an array element value from a JsonArray. + JsonNode firstDateAvailable = datesAvailable[0]; + Console.WriteLine($"Type={firstDateAvailable.GetType()}"); + Console.WriteLine($"JSON={firstDateAvailable.ToJsonString()}"); + //output: + //Type = System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement] + //JSON = "2019-08-01T00:00:00" + + // Get a typed value by chaining references. + int coldHighTemperature = (int)forecastNode["TemperatureRanges"]["Cold"]["High"]; + Console.WriteLine($"TemperatureRanges.Cold.High={coldHighTemperature}"); + //output: + //TemperatureRanges.Cold.High = 20 + + // Parse a JSON array + var datesNode = JsonNode.Parse(@"[""2019-08-01T00:00:00"",""2019-08-02T00:00:00""]"); + JsonNode firstDate = datesNode[0].GetValue(); + Console.WriteLine($"firstDate={ firstDate}"); + //output: + //firstDate = "2019-08-01T00:00:00" + } + } +} diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodePOCOExample.cs b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodePOCOExample.cs new file mode 100644 index 0000000000000..c13fa15425477 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodePOCOExample.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace JsonNodePOCOExample +{ + public class TemperatureRanges : Dictionary + { + } + + public class HighLowTemps + { + public int High { get; set; } + public int Low { get; set; } + } + public class Program + { + public static DateTime[] DatesAvailable { get; set; } + + public static void Main() + { + string jsonString = + @"{ + ""Date"": ""2019-08-01T00:00:00"", + ""Temperature"": 25, + ""Summary"": ""Hot"", + ""DatesAvailable"": [ + ""2019-08-01T00:00:00"", + ""2019-08-02T00:00:00"" + ], + ""TemperatureRanges"": { + ""Cold"": { + ""High"": 20, + ""Low"": -10 + }, + ""Hot"": { + ""High"": 60, + ""Low"": 20 + } + } +} +"; + // Parse all of the JSON. + JsonNode forecastNode = JsonNode.Parse(jsonString); + + // Get a subsection and deserialize it into a custom type. + JsonObject temperatureRangesObject = forecastNode["TemperatureRanges"].AsObject(); + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); + temperatureRangesObject.WriteTo(writer); + writer.Flush(); + TemperatureRanges temperatureRanges = + JsonSerializer.Deserialize(stream.ToArray()); + + Console.WriteLine($"Cold.Low={temperatureRanges["Cold"].Low}, Hot.High={temperatureRanges["Hot"].High}"); + // output: + //Cold.Low=-10, Hot.High=60 + + // Get a subsection and deserialize it into an array. + JsonArray datesAvailable = forecastNode["DatesAvailable"].AsArray(); + Console.WriteLine($"DatesAvailable[0]={datesAvailable[0]}"); + // output: + //DatesAvailable[0]=8/1/2019 12:00:00 AM + } + } +} + + diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/Program.cs b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/Program.cs new file mode 100644 index 0000000000000..00b927573d260 --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace SystemTextJsonSamples +{ + public class Program + { + static void Main() + { + Console.WriteLine("\n============================= From string example\n"); + JsonNodeFromStringExample.Program.Main(); + Console.WriteLine("\n============================= From object example\n"); + JsonNodeFromObjectExample.Program.Main(); + Console.WriteLine("\n============================= POCO example\n"); + JsonNodePOCOExample.Program.Main(); + Console.WriteLine("\n============================= Average Grades example\n"); + JsonNodeAverageGradeExample.Program.Main(); + } + } +} diff --git a/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/SystemTextJsonSamples.csproj b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/SystemTextJsonSamples.csproj new file mode 100644 index 0000000000000..b8993034da33a --- /dev/null +++ b/docs/standard/serialization/snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/SystemTextJsonSamples.csproj @@ -0,0 +1,9 @@ + + + + Exe + net6.0 + SystemTextJsonSamples.Program + + + diff --git a/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md b/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md index ebd2f6d02f52d..c71c9adc1749e 100644 --- a/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md +++ b/docs/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to.md @@ -685,7 +685,7 @@ During deserialization, `Newtonsoft.Json` adds objects to a collection even if t provides the ability to parse and build a **read-only** Document Object Model (DOM) from existing JSON payloads. The DOM provides random access to data in a JSON payload. The JSON elements that compose the payload can be accessed via the type. The `JsonElement` type provides APIs to convert JSON text to common .NET types. `JsonDocument` exposes a property. :::zone pivot="dotnet-6-0" -Documentation for mutable DOM support is being developed. Until it's added, see the [.NET 6 Preview 4 announcement](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-4/#system-text-json-writable-dom-feature). +Starting in .NET 6, you can parse and build a **mutable** DOM from existing JSON payloads by using the type and other types in the namespace. For more information, see [Use `JsonNode`](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md#use-jsonnode). :::zone-end ### JsonDocument is IDisposable @@ -738,7 +738,7 @@ Searches for JSON tokens using `JObject` or `JArray` from `Newtonsoft.Json` tend * Use the built-in enumerators ( and ) rather than doing your own indexing or loops. * Don't do a sequential search on the whole `JsonDocument` through every property by using `RootElement`. Instead, search on nested JSON objects based on the known structure of the JSON data. For example, if you're looking for a `Grade` property in `Student` objects, loop through the `Student` objects and get the value of `Grade` for each, rather than searching through all `JsonElement` objects looking for `Grade` properties. Doing the latter will result in unnecessary passes over the same data. -For a code example, see [Use JsonDocument for access to data](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md#use-jsondocument-for-access-to-data). +For a code example, see [Use JsonDocument](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md#use-jsondocument). ## Utf8JsonReader compared to JsonTextReader diff --git a/docs/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md b/docs/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md index 260973f15d18c..95a0fbec39327 100644 --- a/docs/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md +++ b/docs/standard/serialization/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md @@ -1,7 +1,7 @@ --- -title: How to use the JSON DOM, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json -description: "Learn how to use the JSON DOM, Utf8JsonReader, and Utf8JsonWriter." -ms.date: 01/19/2021 +title: How to use a JSON DOM, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json +description: "Learn how to use a JSON DOM, Utf8JsonReader, and Utf8JsonWriter." +ms.date: 07/28/2021 no-loc: [System.Text.Json, Newtonsoft.Json] zone_pivot_groups: dotnet-version dev_langs: @@ -15,22 +15,18 @@ helpviewer_keywords: ms.topic: how-to --- -# How to use the DOM, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json - -:::zone pivot="dotnet-6-0,dotnet-5-0,dotnet-core-3-1" - provides the ability to build a read-only Document Object Model (DOM) by using `Utf8JsonReader`. The DOM provides random access to data in a JSON payload. The JSON elements that compose the payload can be accessed via the type. The `JsonElement` type provides array and object enumerators along with APIs to convert JSON text to common .NET types. `JsonDocument` exposes a property. -:::zone-end +# How to use a DOM, Utf8JsonReader, and Utf8JsonWriter in System.Text.Json :::zone pivot="dotnet-6-0" -Documentation for mutable DOM support is being developed. Until it's added, see the [.NET 6 Preview 4 announcement](https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-4/#system-text-json-writable-dom-feature). +> [!IMPORTANT] +> Some information relates to prerelease product that may be substantially modified before it's released. Microsoft makes no warranties, express or implied, with respect to the information provided here. :::zone-end -:::zone pivot="dotnet-6-0,dotnet-5-0,dotnet-core-3-1" - is a high-performance, low allocation, forward-only reader for UTF-8 encoded JSON text, read from a `ReadOnlySpan` or `ReadOnlySequence`. The `Utf8JsonReader` is a low-level type that can be used to build custom parsers and deserializers. The method uses `Utf8JsonReader` under the covers. The `Utf8JsonReader` can't be used directly from Visual Basic code. For more information, see [Visual Basic support](system-text-json-how-to.md#visual-basic-support). - - is a high-performance way to write UTF-8 encoded JSON text from common .NET types like `String`, `Int32`, and `DateTime`. The writer is a low-level type that can be used to build custom serializers. The method uses `Utf8JsonWriter` under the covers. +This article shows how to use: -The following sections show how to use these APIs for reading and writing JSON. +* A [JSON Document Object Model (DOM)](#json-dom-choices) for random access to data in a JSON payload. +* The [`Utf8JsonWriter`](#use-utf8jsonwriter) type for building custom serializers. +* The [`Utf8JsonReader`](#use-utf8jsonreader) type for building custom parsers and deserializers. > [!NOTE] > The article about migrating from Newtonsoft has more information about these APIs. See the following sections in that article: @@ -39,7 +35,70 @@ The following sections show how to use these APIs for reading and writing JSON. > * [Utf8JsonReader compared to JsonTextReader](system-text-json-migrate-from-newtonsoft-how-to.md#utf8jsonreader-compared-to-jsontextreader) > * [Utf8JsonWriter compared to JsonTextWriter](system-text-json-migrate-from-newtonsoft-how-to.md#utf8jsonwriter-compared-to-jsontextwriter) -## Use JsonDocument for access to data +## JSON DOM choices + +Working with a DOM is an alternative to deserialization: + +* When you don't have a type to deserialize into. +* When the JSON you receive doesn't have a fixed schema and must be inspected to know what it contains. + +`System.Text.Json` provides two ways to build a JSON DOM: + +* provides the ability to build a read-only DOM by using `Utf8JsonReader`. The JSON elements that compose the payload can be accessed via the type. The `JsonElement` type provides array and object enumerators along with APIs to convert JSON text to common .NET types. `JsonDocument` exposes a property. For more information, see [Use JsonDocument](#use-jsondocument) later in this article. + +:::zone pivot="dotnet-6-0" + +* and the classes that derive from it in the namespace provide the ability to create a mutable DOM. The JSON elements that compose the payload can be accessed via the , , , , and types. For more information, see [Use `JsonNode`](#use-jsonnode) later in this article. + +Consider the following factors when choosing between `JsonDocument` and `JsonNode`: + +* The `JsonNode` DOM can be changed after it's created. The `JsonDocument` DOM is immutable. +* The `JsonDocument` DOM provides faster access to its data. + +:::zone-end + +:::zone pivot="dotnet-5-0,dotnet-core-3-1" + +* Starting in .NET 6, and the classes that derive from it in the namespace provide the ability to create a mutable DOM. For more information, see the [.NET 6 version of this article](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md?pivots=dotnet-6-0). + +:::zone-end + +:::zone pivot="dotnet-6-0" + +## Use `JsonNode` + +The following example shows how to use and the other types in the namespace to: + +* Create a DOM from a JSON string +* Write JSON from a DOM. +* Get a value, object, or array from a DOM. + +:::code language="csharp" source="snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromStringExample.cs"::: + +The following example shows how to: + +* Create a DOM by using object initializers. +* Make changes to a DOM. + +:::code language="csharp" source="snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeFromObjectExample.cs"::: + +The following example shows how to navigate to a subsection of a JSON tree and deserialize a custom type or read an array from that subsection. + +:::code language="csharp" source="snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodePOCOExample.cs"::: + +The following example selects a JSON array that has integer values and calculates an average value: + +:::code language="csharp" source="snippets/system-text-json-use-dom-utf8jsonreader-utf8jsonwriter/csharp/JsonNodeAverageGradeExample.cs"::: + +The preceding code: + +* Calculates an average grade for objects in a `Students` array that have a `Grade` property. +* Assigns a default grade of 70 for students who don't have a grade. +* Gets the number of students from the `Count` property of `JsonArray`. + +::: zone-end + +## Use `JsonDocument` The following example shows how to use the class for random access to data in a JSON string: @@ -62,7 +121,7 @@ Here's an example of the JSON that this code processes: :::code language="json" source="snippets/system-text-json-how-to/csharp/GradesPrettyPrint.json"::: -## Use JsonDocument to write JSON +### Use `JsonDocument` to write JSON The following example shows how to write JSON from a : @@ -83,14 +142,16 @@ The result is the following pretty-printed JSON output: :::code language="json" source="snippets/system-text-json-how-to/csharp/GradesPrettyPrint.json"::: -## Use Utf8JsonWriter +## Use `Utf8JsonWriter` + + is a high-performance, low allocation, forward-only reader for UTF-8 encoded JSON text, read from a `ReadOnlySpan` or `ReadOnlySequence`. The `Utf8JsonReader` is a low-level type that can be used to build custom parsers and deserializers. The method uses `Utf8JsonReader` under the covers. The `Utf8JsonReader` can't be used directly from Visual Basic code. For more information, see [Visual Basic support](system-text-json-how-to.md#visual-basic-support). The following example shows how to use the class: :::code language="csharp" source="snippets/system-text-json-how-to/csharp/Utf8WriterToStream.cs" id="Serialize"::: :::code language="vb" source="snippets/system-text-json-how-to/vb/Utf8WriterToStream.vb" id="Serialize"::: -## Use Utf8JsonReader +## Use `Utf8JsonReader` The following example shows how to use the class: @@ -99,7 +160,7 @@ The following example shows how to use the .