Home > Enterprise >  How to copy a JSON value using .NET 6 JsonNode in System.Text.Json.Nodes namespace?
How to copy a JSON value using .NET 6 JsonNode in System.Text.Json.Nodes namespace?

Time:02-18

I have a simple JSON I would like to rename (probably by copying then removing) a key. .NET Fiddle:

        const string input = @"{ ""foo"": [{""a"": 1}, {""b"": null}] }";
        var node = JsonNode.Parse(input);
        Console.WriteLine(node); // Output: the JSON
        Console.WriteLine(node["foo"][0]["a"]); // Output: 1
        
        // How to copy/rename?
        node["bar"] = node["foo"];
        Console.WriteLine(node["bar"][0]["a"]); // Should be 1
{
  "foo": [
    {
      "a": 1
    },
    {
      "b": null
    }
  ]
}

How do I copy/rename it? I tried using node["foo"].AsValue() but it doesn't work as well. The error is

The node must be of type 'JsonValue'


I just found a workaround by converting to JSON string and parsing it again but I hope there is a better way to do it.

node["bar"] = JsonNode.Parse(node["foo"].ToJsonString());

CodePudding user response:

So what you have is a JSON object, so JsonNode.Parse will really return a JsonObject, so first we need to cast it:

var jsonObject = JsonNode.Parse(input)!.AsObject(); // null check omitted for brevity

Now let's do the rename. A rename is really a removal of the object with the old property followed by readding it with the new name. So:

var node = jsonObject["foo"]; // take a reference to foo so we don't lose it
jsonObject.Remove("foo"); // detach from parent
jsonObject.Add("bar", node); // attach with new name

The reason why you need to first remove it and then add it back, is that JsonNode objects have parents, and you're not allowed to add a node to an object or array if it already has a parent. So you first need to detach it from its parent with Remove, and then you can add it back under the new name with Add.

Now, this modifies the original JSON object. If you want to instead keep the original intact you'll have to clone it first. This is a problem, because there is no way to clone a JsonNode. You could do this:

var clone = new JsonObject(jsonObject);

And it will compile, but then at runtime you'll hit an exception because this just adds the nodes from one object to the next and as we've already established you can't add an object that already has a parent to another one. You would have to first clone each child node and add those instead.

As far as I'm aware, the JsonNodes namespace has no way to do a deep clone of JSON nodes. It seems like an oversight. You could either do it by hand by recursively enumerating the nodes in the document and creating them, or just parse the document twice.

CodePudding user response:

Your question update adds "bar" to an existing node object. if you want to create a new object that is a copy of an existing node object, you can make it 2 main ways

  1. The simpliest and most efficient way is to change json string
    string newInput = input.Replace("\"foo\":","\"bar\":");
    var newNode=JsonNode.Parse(newInput);
  1. Or create new json object
var newNode = new JsonObject( new[] { KeyValuePair.Create<string,JsonNode>("bar", 
               JsonNode.Parse(node["foo"].ToJsonString()) )});

result

{
  "bar": [
    {
      "a": 1
    },
    {
      "b": null
    }
  ]
}
  • Related