1

The new mutable JsonNode type has neither a Clone() nor a copy-constructor. How can I make a copy of one?

I tried to duplicate a shallow JsonObject (one filled with primitive key-value pairs) using new JsonObject(original), but even this doesn't work (it throws InvalidOperationException because a JsonNode cannot be shared between two JSON trees).

Qwertie
  • 16,354
  • 20
  • 105
  • 148

2 Answers2

2

You can clone it indirectly by converting it to JSON, and converting it back to JsonNode.

The quick & dirty way:

var copy = JsonNode.Parse(original.ToJsonString());

It should be noted that JsonNode reads and writes JSON in UTF-8 format, so this method is inefficient partly because it'll convert to UTF-16 and back (and partly because it's parsing, which we seemingly can't avoid). Here is a more efficient method:

public static JsonNode? Clone(this JsonNode? node) {
    if (node == null)
        return null;

    // Yes, we're creating three garbage objects (array + two writers) but
    // the alternative `JsonNode.Parse(original.ToJsonString())` is worse.
    var buf = new ArrayBufferWriter<byte>(512);
    var jwriter = new Utf8JsonWriter(buf);
    node.WriteTo(jwriter);
    jwriter.Flush(); // I don't know why this is necessary, but it is
    return JsonNode.Parse(buf.WrittenSpan);
}

[return: NotNullIfNotNull("node")]
public static JsonObject? Clone(this JsonObject? node)
    => (JsonObject?) Clone((JsonNode) node);

[return: NotNullIfNotNull("node")]
public static JsonArray? Clone(this JsonArray? node)
    => (JsonArray?) Clone((JsonNode)node);

(this does lack an array-pooling optimization used by ToJsonString, which relies on an internal class we don't have access to. It's still better.)


Unit test:

[Test]
public void JsonNodeCloneTest()
{
    var json = JsonNode.Parse(@"{
        ""A"": 12345.67890,
        ""B"": [ ""string"", false, true, null, { ""N"": ""no"" } ]
    }")!;
    var jsonString1 = json.ToJsonString();
            
    var json2 = json.Clone(); // method under test
            
    var jsonString2 = json2.ToJsonString();

    Assert.AreEqual(jsonString1, jsonString2);
}
Qwertie
  • 16,354
  • 20
  • 105
  • 148
0

Note: my solution is in JAVA

I have copied my "valueNode" during iteration and made a copy of it using ObjectMapper now "copyJsonNode" is the replica of "valueNode" which i need for further implementation.

if (entry.getKey().equalsIgnoreCase("admin")) {

            JsonNode valueNode = entry.getValue();
            
            String copyjson = valueNode.toString();

            ObjectMapper objectMapper = new ObjectMapper();

            JsonNode copyJsonNode = objectMapper.readTree(copyjson);

           ....}
Shaik Md
  • 597
  • 4
  • 8
  • 22