Confusion in getting Parent from JToken

2020-08-23 16:28发布

问题:

I have the following JSON document stored in a text file

{
    "attributes": {"attr0":"value0"},
    "children" : {
        "ProductA" : {
            "attributes": {"attr1":"value1", "attr2":"value2"}, 
            "children" : {
                "ProductC":{
                    "attributes": {"attr3":"value3", "attr4":"value4"}, 
                    "children" : {}, 
                    "referencedChildren" : {}
                }
            }, 
            "referencedChildren" : {}
        }, 
        "ProductB" : {
            "attributes": {"attr5":"value5", "attr6":"value6"}, 
            "children" : {}, 
            "referencedChildren" : {}
        }
    },
    "referencedChildren" : {}
}

I have written this code in C# using NewtonSoft JSon.NET Library

string content = File.ReadAllText(@"c:\temp\foo.txt");
JToken token = JToken.Parse(content);
JToken p2 = token["children"]["ProductA"]["children"]["ProductC"];

This works and I get the node for p2.

However if I want the node for ParentA from the p2 node. I have to say

JToken p1 = p2.Parent.Parent.Parent.Parent.Parent;
Console.WriteLine(((JProperty)p1).Name);

The code above prints "ProductA". But the the confusing part is that why do I have to call parent 5 times.

When I look at my document, I can see that "children" is the parent of "ProductC" and then "ProductA" is the parent of children. Therefore 2 calls to Parent should have got me ParentA.

Why do I need 5 calls?

回答1:

The hierarchy you're traversing is how Json.net structures the objects, it is not representative of the json string itself.

Relative to the ProductA object (well, one up), this is how you get down to ProductC:

JProperty: "ProductA"
 -> JObject (ProductA object)
     -> JProperty: "children"
         -> JObject (children object)
             -> JProperty: "ProductC"
                 -> JObject (ProductC object)  *you are here

So if you look at it this way, you should see that you are actually accessing the JProperty "ProductA" (5 parents up), not the object itself. As you might have noticed, JObjects do not have a name, you're getting the name of the JProperty.

I can't tell you how exactly you can access it as it is described in the json string, it doesn't appear to be an option. But you could of course write some helper methods to get them for you.

Here's one implementation to get the parent object. I don't know what other JTokens we'd encounter that we'd want to skip, but this is a start. Just pass in the token you want to get the parent of. Pass in an optional parent number to indicate which parent you want.

JToken GetParent(JToken token, int parent = 0)
{
    if (token == null)
        return null;
    if (parent < 0)
        throw new ArgumentOutOfRangeException("Must be positive");

    var skipTokens = new[]
    {
        typeof(JProperty),
    };
    return token.Ancestors()
        .Where(a => skipTokens.All(t => !t.IsInstanceOfType(a)))
        .Skip(parent)
        .FirstOrDefault();
}


标签: c# json json.net