2

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).

2 Answers 2

4

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);
}
Sign up to request clarification or add additional context in comments.

Comments

-3

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);

           ....}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.