从JToken获得Parent的困惑

时间:2013-12-12 00:09:14

标签: c# json json.net

我将以下JSON文档存储在文本文件中

{
    "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" : {}
}

我使用NewtonSoft JSon.NET库在C#中编写了这段代码

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

这有效,我得到p2的节点。

但是,如果我想从ParentA节点获取p2节点。我不得不说

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

上面的代码打印"ProductA"。但令人困惑的部分是,为什么我必须打电话给父母5次。

当我查看我的文档时,我可以看到"children""ProductC"的父级,然后"ProductA"是孩子的父级。因此,对Parent的2次调用应该让我ParentA

为什么我需要5个电话?

1 个答案:

答案 0 :(得分:8)

您正在遍历的层次结构是Json.net如何构造对象,它不代表json字符串本身。

相对于ProductA对象(嗯,一个向上),这就是你到达ProductC的方式:

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

因此,如果您以这种方式看待它,您应该看到您实际上正在访问JProperty“ProductA”(5个父母向上),而不是对象本身。您可能已经注意到,JObject没有名称,您将获得JProperty的名称。

我无法告诉你如何在json字符串中描述它如何访问它,它似乎不是一个选项。但是你当然可以编写一些辅助方法来为你获取它们。

这是获取父对象的一个​​实现。我不知道我们想要跳过的其他JTokens,但这是一个开始。只需传入您想要获得父级的令牌即可。传入可选的父母编号,以指示您想要的父母。

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();
}