在JSON.Net中发现父节点的棘手业务

时间:2013-12-12 03:11:06

标签: json json.net

这是我的问题

JSON.NET - Confusion in getting Parent from JToken

我仍然无法理解如何在JSON.NET中发现父级

所以现在我有了这个JSON文档

{
    "attributes": {"attr0":"value0"},
    "children" : {
        "ProductA" : {
            "attributes": {"attr1":"value1", "attr2":"value2"}, 
            "children" : {
                "ProductC":{
                    "attributes": {"attr3":"value3", "attr4":"value4"}, 
                    "children" : {
                        "ProductD":{
                            "attributes": {"attr7":"value7", "attr8":"value8"}, 
                            "children" : {
                                "ProductE":{
                                    "attributes": {"attr9":"value9", "attr10":"value10"}, 
                                    "children" : {}, 
                                    "referencedChildren" : {}
                                }
                            }, 
                            "referencedChildren" : {}
                        }
                    }, 
                    "referencedChildren" : {}
                }
            }, 
            "referencedChildren" : {}
        }, 
        "ProductB" : {
            "attributes": {"attr5":"value5", "attr6":"value6"}, 
            "children" : {}, 
            "referencedChildren" : {}
        }
    },
    "referencedChildren" : {}
}

基于此,我编写了这段代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;

namespace JSonTest {
  class Program {
    static void Main(string[] args) {
      string content = File.ReadAllText(@"c:\temp\foo.txt");
      JObject token = JObject.Parse(content);
      JToken p2 = token["children"]["ProductA"]["children"]["ProductC"]["children"]["ProductD"]["children"]["ProductE"];
      JToken parent = p2;
      do {
        parent = GetParentModule(parent);
        Console.WriteLine(((JProperty)parent).Name);
      } while (parent != null);
    }

    private static JToken GetParentModule(JToken token) {
      JToken retVal = token;
      int i = 5;
      while (i > 0) {
        retVal = retVal.Parent;
        if (retVal == null) {
          break;
        } else {
          --i;
        }
      }
      return retVal;
    }
  }    
}

当我运行此代码时,第一次调用GetParentModule返回“ParentD”...但第二次调用不返回“ParentC”

我的目标是,当我在ProductE上调用GetParentModule时,它返回ProductD,当我在ProductD上调用GetParentModule时,它返回ProductC,当我在ParentC上调用GetParentModule时,它返回ProductA。

在我之前的帖子中,我发现有5次调用Parent,正确地将父项返回给我。但在后续调用中,我看到对Parent的“4”调用返回“ProductC”。

您能否解释一下发生了什么以及如何成功地走上父层次结构?

1 个答案:

答案 0 :(得分:8)

我认为你很困惑,因为有两个因素对你不利:

  1. Json.Net使用的实际内存中层次结构与JSON的心智模型不匹配。
  2. 语法token["property"]通过抽象实际结构来简化JSON的向下遍历,以更好地适应您的心理模型。但是,向上导航没有这么方便,所以你会接触到所有额外的层。
  3. 让我们以您的JSON为例,详细了解一下正在发生的事情。假设我们有这个JSON:

    {
        "children": {
            "ProductA": {
                "children": {
                    "ProductC": {
                        "attribute": "some stuff"
                    }
                }
            }
        }
    }
    

    在你的心理模型中你有这个:

    • 包含的顶级对象(没有名称)
      • 一个名为“children”的对象,其中包含
        • 一个名为“ProductA”的对象,其中包含
          • 一个名为“children”的对象,其中包含
            • 一个名为“ProductC”的对象,其中包含
              • 属性值为“some stuff”。

    但是Json.Net对象模型的工作原理如下:

    • JObjectJProperty个对象的集合。
    • JProperty是名称 - 值对,其中名称为字符串,值为JObjectJValueJArray
    • JObject的孩子是JProperties
    • JProperty的孩子是其价值。

    所以JSON的实际内存表示是:

    • 顶级JObject,其中包含
      • 一个名为“children”的JProperty,其中包含
        • 一个包含的JObject
          • 一个名为“ProductA”的JProperty,其中包含
            • 一个包含的JObject
              • 一个名为“children”的JProperty,其中包含
                • 一个包含的JObject
                  • 一个名为“ProductC”的JProperty,其中包含
                    • 一个包含的JObject
                      • 一个名为“attribute”的JProperty,其中包含
                        • 一个字符串值为“some stuff”的JValue。
    所以你可以看到你所认为的单个命名对象实际上是一个未命名的JObject包装在名为JProperty的内部。这有助于使对象层次结构的深度达到预期的两倍。

    当你做这样的事情时:

    JObject productA = (JObject)top["children"]["ProductA"];
    

    这些额外的图层对您隐藏。它看起来就好像每个对象(或属性)直接嵌套在它上面的对象中。但不要被愚弄。在幕后,这个索引器语法实际上只是这个等效代码的快捷方式:

    JObject productA = (JObject)top.Children<JProperty>()
                                   .First(prop => prop.Name == "children")
                                   .Value
                                   .Children<JProperty>()
                                   .First(prop => prop.Name == "ProductA")
                                   .Value;
    

    希望现在应该清楚发生了什么,我们可以回到你真正的问题,即如何上升链并获得理想的结果。例如,假设我们有对产品C的引用,并且我们想要获得产品A(或者更确切地说,我们引用了JObject,它是名为“ProductC”的JProperty的值,我们想要上升获取名称为“ProductA”的JProperty的值的链。我们怎么做?

    那么,再次,如果你看看实际的Json.Net结构,你可以看到它的模式。您已识别为“产品”的每个JObject都在JProperty中,其中“有趣”的名称不是“子”。如果该产品具有“父产品”,则它将具有名称​​ “children”的祖先JProperty。那个JProperty的父母就是你想要的人。

    换句话说,你需要做的就是向上走,直到找到第一个名为“children”的JProperty,然后取出那个JProperty的父级,那应该是你正在寻找的JObject。 p>

    在代码中:

    private static JToken GetParentModule(JToken token)
    {
        while (token != null && 
              (token.Type != JTokenType.Property ||
              ((JProperty)token).Name != "children"))
        {
            token = token.Parent;
        }
        return (token != null ? token.Parent : null);
    }