Skip to content

[S.T.J] Setting same JsonNode in an JsonArray on same position again throws exception #113966

Open
@warappa

Description

@warappa

Description

Assigning a JsonNode to the same parent again throws an exception stating that the node already has a parent. If it would be re-parented then this would be expected. But if the new parent is old parent, then this should not fail but treat it as a no-op.

In local code this is no problem, because then you just refactor the assignment. But if you do a library and let the developer return a transformed or - as in our case - the original JsonNode, then this is problematic, because now you have to check for reference equality and avoid the assignment.

Investigation

This is the involved code:

private protected override void SetItem(int index, JsonNode? value)
{
value?.AssignParent(this);
DetachParent(List[index]);
List[index] = value;
}

calling AssignParent which throws an exception if parent has any value (even if it is the same):
if (Parent != null)
{
ThrowHelper.ThrowInvalidOperationException_NodeAlreadyHasParent();
}

For properties of JsonObject this seems to be no problem as the following code does not throw:

var obj = new JsonObject();
var node = JsonValue.Create(1);
obj["child"] = node;
obj["child"] = node; // no exception 👍

Those two behaviors are different but the JsonObject property no-op assignment behavior makes sense.

Reproduction Steps

var array = new JsonArray();
var node= JsonValue.Create(1); // or new JsonObject();
array.Add(node);
array[0] = node; // throws "System.InvalidOperationException: 'The node already has a parent.'"

Expected behavior

No exception should be thrown - effectivly a no-op.

Actual behavior

Throws exception

System.InvalidOperationException: 'The node already has a parent.'

Regression?

Unknown

Known Workarounds

This is a undesirable workaround. Instead of just writing

var array = new JsonArray();
var node = new JsonObject();
array.Add(node);
...
// somewhere later on
array[0] = SomeCodeReturningANewOrSameJsonNode(node);

one needs to carefully write

var array = new JsonArray();
var node = new JsonObject();
array.Add(node);
...
// somewhere later on
var temp = SomeCodeReturningANewOrSameJsonNode(array[0]);
if (!ReferenceEquals(array[0], temp))
{
    array[0] = temp;
}

Configuration

.NET 9
Windows 11

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-System.Text.JsonenhancementProduct code improvement that does NOT require public API changes/additions

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions