如何在 System.Text.Json.Nodes 命名空间中使用 .NET 6 JsonNode 复制 JSON 值?

Luk*_* Vo 7 c# json deserialization system.text.json .net-6.0

我有一个简单的 JSON,我想重命名(可能通过复制然后删除)一个密钥。.NET 小提琴

        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
Run Code Online (Sandbox Code Playgroud)
{
  "foo": [
    {
      "a": 1
    },
    {
      "b": null
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

如何复制/重命名它?我尝试使用node["foo"].AsValue()但效果不佳。错误是

该节点必须是“JsonValue”类型


我刚刚找到了一个解决方法,通过转换为 JSON 字符串并再次解析它,但我希望有更好的方法来做到这一点。

node["bar"] = JsonNode.Parse(node["foo"].ToJsonString());
Run Code Online (Sandbox Code Playgroud)

Eti*_*tel 13

所以你拥有的是一个 JSON 对象,所以JsonNode.Parse实际上会返回一个JsonObject,所以首先我们需要转换它:

var jsonObject = JsonNode.Parse(input)!.AsObject(); // null check omitted for brevity
Run Code Online (Sandbox Code Playgroud)

现在让我们进行重命名。重命名实际上是删除具有旧属性的对象,然后使用新名称读取它。所以:

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
Run Code Online (Sandbox Code Playgroud)

之所以需要先删除它,然后再添加回来,是因为JsonNode对象有父级,如果对象或数组已经有父级,则不允许将节点添加到对象或数组中。因此,您首先需要使用 将其与其父级分离Remove,然后可以使用 新名称将其添加回来Add

现在,这会修改原始 JSON 对象。如果您想保持原始版本完整,则必须先克隆它。这是一个问题,因为没有办法克隆JsonNode. 你可以这样做:

var clone = new JsonObject(jsonObject);
Run Code Online (Sandbox Code Playgroud)

它会编译,但在运行时您会遇到异常,因为这只是将一个对象的节点添加到下一个对象,并且正如我们已经确定的那样,您无法将已经具有父级的对象添加到另一个对象。您必须首先克隆每个子节点并添加它们。

据我所知,JsonNodes命名空间无法对 JSON 节点进行深度克隆。这似乎是一个疏忽。您可以通过递归枚举文档中的节点并创建它们来手动完成此操作,也可以仅解析文档两次。