如何访问嵌套json对象的父级

时间:2018-06-13 22:16:34

标签: python json parent-child parent children

我有一个任意嵌套的JSON对象(使用json.load解析),由dicts,lists,primitives等组成。我首先使用递归遍历深度并以linux fs格式跟踪节点的路径(对于列表,我将/ element附加到路径,因此在特定路径上可以有多个对象),如下所示:

def traverse(node, path =''):
   if isinstance(node, dict):
      for key in node:
         traverse(node[key], path+'/'+key)
   elif isinstance(node, list):
      for elem in node:
         traverse(elem,path+'/element')

每个节点可以包含一个字符串,需要使用可以存在于树中任何位置的对象的值来填充,在当前节点的相对路径中引用,例如:" {../../ key} {./child1/child2/key}"。
问题:我可以访问当前节点的子节点的值,但是我无法直接访问当前节点的父节点。
我认为的解决方案:我认为有一个解决方案是拥有一个元组列表(子节点,父节点)并将当前节点与我即将递归的子节点一起存储,然后搜索当我需要上去时反向列出。这有点危险,因为如果孩子是原始值,那么将等于具有相同值和类型的任何其他孩子,因此我可能检索错误的父母,但我认为反过来通过列表应该照顾那对吗? 我认为一个不同的解决方案是使用一个字典,其中key是子路径并且是父节点的值。我认为这应该更好,因为唯一的时间路径冲突是列表元素,但它们都有相同的父级所以我认为应该没问题。

还有其他建议吗?或者对这两种解决方案的任何评论? 谢谢

2 个答案:

答案 0 :(得分:1)

回到dict的父级(或任何没有后向指针的递归结构)的唯一方法是在遍历时记住它。

但请注意,您已经这样做了:您的path字符串是从顶部到当前节点的路径。

让我们编写一个使用路径的函数:

def follow(head, path):
    if not path:
        return head
    first, _, rest = path.partition('/')
    return follow(head[first], rest)

当然,建立path作为键的元组而不是字符串可能更好,所以我们不必将它们分开(所以我们没有如果任何密钥可能包含/等,请担心转义或引用;);你最后总是可以join

path构建为节点(或键 - 节点对)的元组而不仅仅是键可能更好,因此我们可以在常量时间内访问父节点{{1}而不是以path[-1]为对数的时间。但这实际上取决于你实际上要做的事情;您的真实代码可能不会遍历树,构建到每个节点的路径,然后对它们不执行任何操作。

然而,解决这个问题的一个非常好的方法是将遍历内部转换为:make follow(head, path)一个迭代器:

traverse

现在我们可以循环遍历def traverse(node, path =''): if isinstance(node, dict): for key in node: yield from traverse(node[key], path+'/'+key) elif isinstance(node, list): for elem in node: yield from traverse(elem,path+'/element') yield (node, path) 以执行我们想要的任何操作作为后期深度优先遍历:

traverse

现在您可以轻松更改它以产生for node, path in traverse(root): # do something (无论node, parent, path是父节点,还是父键,或者您想要的任何内容)。

答案 1 :(得分:0)

在Python中, vanilla 对象(至少那些用于JSON blob的对象,如listdict等)有 no 关系收集和包含它们的对象。

这是有道理的,因为基本上列表可以多次包含相同的对象。此外,对象可以同时存储在字典和集合中。除非你执行某种垃圾收集算法(这在性能方面非常低效,并且将“扫描”覆盖所有对象),因此没有重建的重要方法引用给定对象的对象列表。即使我们要扫描这些对象,由于可能有多个父母,因此决定我们将看到的“”仍然是微不足道的。

在Python中,字典甚至可以包含自身。像:

# example of constructing a datastructure containing itself
some_dict = {}
some_dict['a'] = some_dict

现在我们无休止地递归some_dict,例如some_dict['a']['a']['a'] is some_dict

关键是,由于您以递归方式枚举,因此可以维护一个包含祖先的堆栈。例如:

def traverse(node, path ='', stack=None):
    if stack is None:
        stack = [node]
    else:
        stack.push(self)
    if isinstance(node, dict):
        for key in node:
            traverse(node[key], path+'/'+key, stack)
    elif isinstance(node, list):
        for elem in node:
            traverse(elem,path+'/element', stack)
    stack.pop()

因此,在检查节点之前,每个节点都会在堆栈上推送自己,最后,它会从堆栈中弹出自己。我们以递归方式传递堆栈,因此每个递归调用都可以检查堆栈(不仅是父级,还有整个跟踪到根目录)。