3

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?

1 Answer 1

4

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);
3
  • Thank you for this competent answer. Coming from javascript, adding an object to multiple parents doesn't clone the object. The plan was, to create functions, that do stuff horizontally e.g. for all the parents or all the children. Therefore I added the object to these objects as well...
    – maol
    Commented Mar 27, 2015 at 5:33
  • 1
    You showed me, that I should get deeper knowledge of c# rather than trying to use c# like javascript.
    – maol
    Commented Mar 27, 2015 at 5:36
  • Thank you, a little different, but this helped me finally solve adding a nested object without having to deserialize the whole thing.
    – dsghi
    Commented Sep 6, 2018 at 8:47

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.