Traversing JSON with JsonDocument
System.Text.Json
The System.Text.Json
namespace is fairly new and was introduced when .NET Core 3.0 shipped on 23rd Sept 2019. It includes classes to deal with processing JSON with high-performance, and low-allocating in mind.
A common alternative to this namespace was Newtonsoft’s Json.NET, and it’s still widely used within the .NET ecosystem today.
JsonDocument
The JsonDocument
class within the namespace is responsible for examining the structural content of a JSON value, similar to JToken
within Json.NET.
JsonDocument
is great if you want to inspect the JSON but not serialize it in to a C# POCO (Plain Old CLR Object). I personally find this really useful when testing APIs where I don’t want a deserializer doing some extra magic when verifying the return values of an API.
Traversing JSON
Let’s take the following JSON example below and write some C# using JsonDocument to traverse it.
{
"name": "Joe",
"age": 22,
"canDrive": true,
"contactDetails": {
"email": "[email protected]",
"mobile": "07738277382",
"fax": null
},
"addresses":[
{
"line1": "15 Beer Bottle Street"
},
{
"line1": "Shell Cottage"
}
]
}
To start with we’ll need to parse the JSON string, to do this we can use the static factory method on the JsonDocument
class. If you’re parsing from a stream then you might want to use the ParseAsync
method.
using var jsonDocument = JsonDocument.Parse(json);
The JsonDocument
itself doesn’t really do much for an end consumer, however, it does implement IDisposable
so make sure you wrap it in a using
statement, that way it will get cleaned up after use.
The RootElement
property on the JsonDocument
is the only property on JsonDocument
, This returns back a JsonElement
A JSON document is broken down in to a bunch of elements and properties, an element can be thought of as a value of a property, such as a primitive value (string, number, object, array, boolean, null), object, or array.
In our above example "Joe"
and 22
are JsonElement
’s but also {"email": "[email protected]","mobile": "07738277382", "fax": null }
is also classified as a JsonElement
.
We can fetch these JsonElement
s by calling the GetProperty(string)
method on any JsonElement
, which includes the RootElement
.
var rootElement = jsonDocument.RootElement;
var nameJsonElement = rootElement.GetProperty("name");
var ageJsonElement = rootElement.GetProperty("age");
var contactDetailsJsonElement = rootElement.GetProperty("contactDetails");
var addressesJsonElement = rootElement.GetProperty("addresses");cd
Once we’ve got these properties we can then get values from element.
var name = nameJsonElement.GetString();
var age = ageJsonElement.GetInt32();
Console.WriteLine($"Name: {name}"); // Joe
Console.WriteLine($"Age: {age}"); // 22
Objects are very similar, we can keep traversing down the properties
var contactDetailsJsonElement = rootElement.GetProperty("contactDetails");
var emailJsonElement = contactDetailsJsonElement.GetProperty("email");
var email = emailJsonElement.GetString();
Console.WriteLine($"Email: {email}"); // [email protected]
var mobileJsonElement = contactDetailsJsonElement.GetProperty("mobile");
var mobile = mobileJsonElement.GetString();
Console.WriteLine($"Mobile: {mobile}"); // 07738277382
However we can also enumerate all the properties on the object.
var contactDetailsJsonElement = rootElement.GetProperty("contactDetails");
foreach (var jsonProperty in contactDetailsJsonElement.EnumerateObject())
{
Console.WriteLine($"{jsonProperty.Name}: {jsonProperty.Value}");
}
// email: [email protected]
// mobile: 07738277382
// fax:
Arrays are very similar in how we enumerate them.
var addressesJsonElement = rootElement.GetProperty("addresses");cd
foreach (var jsonElement in addressesJsonElement.EnumerateArray())
{
var line1JsonElement = jsonElement.GetProperty("line1");
var line1 = line1JsonElement.GetString();
Console.WriteLine($"Line 1: {line1}");
Console.WriteLine($"----");
}
// Line 1: 15 Beer Bottle Street
// ----
// Line 1: Shell Cottage
// ----
As you can see everything follows the same simple structure.