This blog post contains some notes to help me remember how to use the System.Text.Json.Nodes namespace in .NET 6 and beyond to modify JSON in memory.
The C# code that I previously migrated from Newtonsoft.Json to System.Text.Json serialized and deserialized between objects and JSON, but never explicitly updated the JSON. Recently, I had a need to update JSON before deserialization and realized that, until .NET 6, System.Text.Json is read-only, and therefore useful only for serialization and deserialization, not for modifying the JSON node tree in memory.
.NET six introduces the System.Text.Json.Nodes namespace for mutable JSON documents. Visual Studio community edition does not support the .NET 6 SDK, so I downloaded a prerelease version of JetBrains Rider that does.
- System.Text.Json.Nodes Namespace | Microsoft Docs
- Provides types for handling an in-memory writeable document object model (DOM) for random access of the JSON elements within a structured view of the data.
- JsonArray Class (System.Text.Json.Nodes) | Microsoft Docs
- Represents a mutable JSON array.
- Object > JsonNode > JsonArray
- JsonNode Class (System.Text.Json.Nodes) | Microsoft Docs
- The base class that represents a single node within a mutable JSON document.
- Object > JsonNode
- JsonObject Class (System.Text.Json.Nodes) | Microsoft Docs
- Represents a mutable JSON object.
- Object > JsonNode > JsonObject
- JsonValue Class (System.Text.Json.Nodes) | Microsoft Docs
- Represents a mutable JSON value.
- Object > JsonNode > JsonValue
The System.Text.Json.Nodes namespace contains classes for in-memory writable structured JSON manipulation. I think of JsonNode as read-only, use JsonObject to update, and I haven’t found a need for JsonArray or JsonValue.
I don’t know if you can convert a JsonElement (read-only in the System.Text.Json namespace) to a JsonNode, but you can parse its raw JSON.
JsonNode jsonNode = JsonNode.Parse(jsonElement.GetRawText());
For demonstration purposes, assume that I have a key named container and I want to move its child keys to the root. So, this:
{ "container": { "move": "this is or was in the container", "this": { "also": "need to be moved" }, "too": "." } }
Would become:
{ "move": "this is or was in the container", "this": { "also": "need to be moved" }, "too": "." }
This test code seems to work correctly.
using System.Text.Json.Nodes; // source JSON to process string jsonString = File.ReadAllText("test.json"); Console.WriteLine(jsonString); // root node (opening curly brace) JsonObject? root = JsonNode.Parse(jsonString)?.AsObject(); // if the root contains no key named "container" JsonNode? containerNode = root?["container"]; if (containerNode == null) { return; } // get the names of the keys under the container key List<string> keys = containerNode.AsObject().Select( child => child.Key).ToList(); // convert read-only JsonNode to writable JsonObject JsonObject container = containerNode.AsObject(); // iterate and move keys from container to root foreach (string key in keys) { JsonNode? move = containerNode[key]; container.Remove(key); root?.Add(key, move); } root?.Remove("container"); Console.WriteLine(root);
Note that at least with Rider using .NET 6 with C# 10, the code for a command line tool apparently does not need a static class declaration and main() method (entry point), so this is actually the complete program (minus the test.json file). And I could still access something named args that I couldn’t see declared, and I think use types defined in the System namespace.
One thought on “.NET 6: Modify JSON in Memory with the System.Text.Json.Nodes Namespace”