Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusion in getting Parent from JToken

Tags:

json

c#

json.net

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?

like image 661
Knows Not Much Avatar asked Dec 12 '13 00:12

Knows Not Much


People also ask

How can I get JProperty from JToken?

You can use the Children<T>() method to get a filtered list of a JToken's children that are of a certain type, for example JObject . Each JObject has a collection of JProperty objects, which can be accessed via the Properties() method. For each JProperty , you can get its Name .

What is JObject and JToken?

The JToken hierarchy looks like this: JToken - abstract base class JContainer - abstract base class of JTokens that can contain other JTokens JArray - represents a JSON array (contains an ordered list of JTokens) JObject - represents a JSON object (contains a collection of JProperties) JProperty - represents a JSON ...

What is a JObject?

JObject. It represents a JSON Object. It helps to parse JSON data and apply querying (LINQ) to filter out required data. It is presented in Newtonsoft.


1 Answers

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();
}
like image 179
Jeff Mercado Avatar answered Oct 13 '22 20:10

Jeff Mercado