如何访问嵌套 json object 的 parent

How to access parent of nested json object

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

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')

每个节点可能包含一个字符串,需要使用 objects 的值填充,这些值可以存在于树中的任何位置,在当前节点的相对路径中引用,例如:“{.. /../key} {./child1/child2/key}".
问题:我可以访问当前节点的children节点的值,但我不能直接访问当前节点的parent。
我认为的解决方案:我认为的一个解决方案是有一个元组列表(child,parent)并将当前节点与 child 我准备递归进去,然后在需要上去的时候反向搜索那个列表。这有点危险,因为如果 child 是原始值,那么它将等于任何其他具有相同值和类型的 children,因此我可能会检索到错误的 parent,但我认为反向浏览列表应该可以解决这个问题,对吧?
我认为一个不同的解决方案是有一个字典,键是 child 的路径,值是 parent 节点。我认为这应该更好用,因为唯一一次路径冲突是与列表元素发生冲突,但它们都具有相同的 parent 所以我认为应该没问题。

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

在 Python 中 vanilla 对象(至少那些用于 JSON blob 的 list, dict,等)与集合和包含它们的对象没有关系。

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

在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):
    <b>if stack is None:
        stack = [node]
    else:
        stack.push(self)</b>
    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)
    <b>stack.pop()</b>

因此,在我们检查节点之前,每个节点都会将自己压入堆栈,最后,它会将自己从堆栈中弹出。我们以递归方式传递堆栈,因此每次递归调用都可以检查堆栈(不仅是父级,还包括直到根的整个路径)。

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

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

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

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

当然,将 path 构建为键元组而不是字符串可能会更好,这样我们就不必将它们分开(因此我们不必担心关于转义或引用(如果任何键可能包含 / 等);你总是可以 join 他们最后。

并且将 path 构建为节点元组(或键-节点对)而不仅仅是键可能会更好,因此我们可以在恒定时间内访问父节点,就像 path[-1] 而不是对数时间 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 来做任何我们想做的事情,作为 post 顺序深度优先遍历:

for node, path in traverse(root):
    # do something

现在您可以轻松更改它以生成 node, parent, path(无论 parent 是父节点,还是父键,或者您想要的任何内容)。

如果你想通过属性值搜索一个节点,并知道该节点的父节点是什么,你可以使用以下方法:

def find_node_callback(node, stack, callback, path=''):
    stack.append(node)
    if callback(node,path,stack):
        return True

    if isinstance(node, dict):
        for key in node:
            if find_node_callback(node[key],stack,callback,path+'/'+key):
                return True
    elif isinstance(node, list):
        for elem in node:
            if find_node_callback(elem,stack, callback,path+'/element'):
                return True

    stack.pop()
    return False

这样称呼它:

def isTargetNode(node,path,stack):
    if node == "value Im searching for":
        return True
    return False

# uses a callback to determine if the node is what you want
a = json.loads(jsonString) 
stack=[]          
if find_node_callback(a,stack,isTargetNode):
    # this array will contain all the parent items of your
    # target node, with the target node value being the last
    # thing stored in the array
    print(stack)