maol
maol

Reputation: 33

nested json objects dont update / inherit using Json.NET

Using the depreciated System.Json, I get the Result I expect (coming from Javascript): The Child gets a GrandChild and all the Parents know about it...

var Parents         = new JsonObject();
var Children        = new JsonObject();

var Parent          = JsonArray.Parse("[]");
Parents.Add("1", Parent);

var Child           = JsonArray.Parse("[]");
Children.Add("1", Child);

var DstParent       = (JsonArray)Parents["1"];
DstParent.Add(Children["1"]);


var DstChild    = (JsonArray)Children["1"];
JsonObject GrandChild   = (JsonObject)JsonArray.Parse("{}");
GrandChild.Add("Age", 15);
DstChild.Add(GrandChild);

var Result = Parents.ToString();

Gives me: "{"1":[[{"Age":15}]]}"

Using Newtonsoft.Json 6.0.8, The Parent is not getting the "hint" that it's Child got a GrandChild.

var Parents         = new JObject();
var Children        = new JObject();

var Parent          = JArray.Parse("[]");
Parents.Add("1", Parent);

var Child           = JArray.Parse("[]");
Children.Add("1", Child);

var DstParent       = (JArray)Parents["1"];
DstParent.Add(Children["1"]);


var DstChild    = (JArray)Children["1"];
var GrandChild  = JObject.Parse("{}");
GrandChild.Add("Age", 15);
DstChild.Add(GrandChild);

Gives me: "{"1":[[]]}"

What am I doing wrong?

Upvotes: 3

Views: 1246

Answers (1)

dbc
dbc

Reputation: 117026

The problem arises because all JToken objects have a Parent property which records their location in the JSON object hierarchy -- but you are trying to add your JArray Child to two different unrelated parents. First you add it to the Children object (which is not actually in the tree of JSON objects you are creating):

        Children.Add("1", Child);

Next you add it to the DstParent array (which is in the tree of JSON objects you are creating):

        DstParent.Add(Children["1"]);

So, what does Json.NET do in this case? It could either:

  1. Throw an exception for trying to create a multiply-parented object, OR
  2. Excise the object from its previous parent and move it to its new parent, OR
  3. Create a clone of the object in its new parent.

As it turns out, it takes option #3: it copies Children["1"] into DstParent. I'm not sure if or where this is documented, but it's apparent from the source code for JContainer - look for InsertItem which calls EnsureParentToken. Thus when you add your grandchild to DstChild you are adding it to the original array not the copy. You can see this by adding the following debug code:

        Debug.WriteLine(object.ReferenceEquals(DstParent[0], DstChild)); //prints False

The simple fix for this is to avoid creating the Children object which is completely unnecessary anyway:

        var parentObj = new JObject();

        var parentArray = new JArray();
        parentObj.Add("1", parentArray);

        var childArray = new JArray();
        parentArray.Add(childArray);

        var grandChild = new JObject();
        grandChild.Add("Age", 15);
        childArray.Add(grandChild);

Upvotes: 4

Related Questions