Skip to content

Commit

Permalink
Document mutable DOM (#24963)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdykstra authored Jul 28, 2021
1 parent 17dec64 commit 269adc3
Show file tree
Hide file tree
Showing 8 changed files with 439 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
// }
// }
//}
}
}
}


Original file line number Diff line number Diff line change
@@ -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<T>.
temperatureInt = forecastNode["Temperature"].GetValue<int>();
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<DateTime>();
Console.WriteLine($"firstDate={ firstDate}");
//output:
//firstDate = "2019-08-01T00:00:00"
}
}
}
Original file line number Diff line number Diff line change
@@ -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<string, HighLowTemps>
{
}

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<TemperatureRanges>(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
}
}
}


Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<StartupObject>SystemTextJsonSamples.Program</StartupObject>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ During deserialization, `Newtonsoft.Json` adds objects to a collection even if t
<xref:System.Text.Json.JsonDocument?displayProperty=fullName> 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 <xref:System.Text.Json.JsonElement> type. The `JsonElement` type provides APIs to convert JSON text to common .NET types. `JsonDocument` exposes a <xref:System.Text.Json.JsonDocument.RootElement> 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 <xref:System.Text.Json.Nodes.JsonNode> type and other types in the <xref:System.Text.Json.Nodes> namespace. For more information, see [Use `JsonNode`](system-text-json-use-dom-utf8jsonreader-utf8jsonwriter.md#use-jsonnode).
:::zone-end

### JsonDocument is IDisposable
Expand Down Expand Up @@ -738,7 +738,7 @@ Searches for JSON tokens using `JObject` or `JArray` from `Newtonsoft.Json` tend
* Use the built-in enumerators (<xref:System.Text.Json.JsonElement.EnumerateArray%2A> and <xref:System.Text.Json.JsonElement.EnumerateObject%2A>) 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

Expand Down
Loading

0 comments on commit 269adc3

Please sign in to comment.