在Python中遍历嵌套字典并获取路径?

4

我有一个类似于字典的数据结构:

{
   "checksum": "b884cbfb1a6697fa9b9eea9cb2054183",
   "roots": {
      "bookmark_bar": {
         "children": [ {
            "date_added": "12989159740428363",
            "id": "4",
            "name": "test2",
            "type": "url",
            "url": "chrome://bookmarks/#1"
         } ],
         "date_added": "12989159700896551",
         "date_modified": "12989159740428363",
         "id": "1",
         "name": "bookmark_bar",
         "type": "folder"
      },
      "other": {
         "children": [ {
            "date_added": "12989159740428363",
            "id": "4",
            "name": "test",
            "type": "url",
            "url": "chrome://bookmarks/#1"
         } ],
         "date_added": "12989159700896557",
         "date_modified": "0",
         "id": "2",
         "name": "aaa",
         "type": "folder"
      },
      "synced": {
         "children": [  ],
         "date_added": "12989159700896558",
         "date_modified": "0",
         "id": "3",
         "name": "bbb",
         "type": "folder"
      }
   },
   "version": 1
}

一切都始于“根”,它们有两种类型的数据:URL和文件夹,它们都是字典。

如果是文件夹,则必须具有键'children',该键的值为列表,我们可以在其中添加更多的URL和文件夹。

现在我想遍历这个嵌套字典,以获取所有子文件夹中的URL,因此我编写了一个函数:

def traverse(dic):
    for i in dic:
        if i['type'] == 'folder':
            for j in traverse(i['children']):
                yield j
        elif i['type'] == 'url':
            yield i

我可以这样使用它:

traverse(dictionary['roots']['bookmark_bar']['children'])

它完美地工作了。但它只生成了一个URL的字典,我不知道它在哪里。 我想要获取路径。我该怎么做?


4
你能否使用缩进对字典进行格式化?并且请删除与理解你的问题无关的内容? - user647772
字典现在可读。 - 比尔盖子
参见:https://dev59.com/x1vUa4cB1Zd3GeqPy_1D https://dev59.com/x1vUa4cB1Zd3GeqPy_1D#16508328 - dreftymac
2个回答

11

我的用例与您略有不同:我需要将表示客户端设置的变量深度JSON结构扁平化为键值对,以便存储到数据库中。我无法让jsbueno的答案起作用,因为我还需要处理没有显式列出或包含子级的情况,因此我修改了它以适应我的需求:

def traverse(dic, path=None):
    if not path:
        path=[]
    if isinstance(dic,dict):
        for x in dic.keys():
            local_path = path[:]
            local_path.append(x)
            for b in traverse(dic[x], local_path):
                 yield b
    else: 
        yield path,dic

最终结果是我可以将这样的一个JSON字符串传递给我的脚本(带有可变深度),它将其转换为嵌套字典:

{
  "servers": {
    "uat": {
      "pkey": true,
      "user": "testval",
      "pass": true
    },
    "dev": {
      "pkey": true,
      "user": "testval",
      "pass": true
    }
  }
}

运行上述生成器会创建一个列表,其输出如下:

([u'servers', u'uat', u'pkey'], True)
([u'servers', u'uat', u'user'], u'testval')
([u'servers', u'uat', u'pass'], True)
([u'servers', u'dev', u'pkey'], True)
([u'servers', u'dev', u'user'], u'testval')
([u'servers', u'dev', u'pass'], True)

使用类似以下方式:

for x in traverse(outobj):
    pprint(('.'.join(x[0]),x[1]))

然后可以将其转换为我所需的键值对格式,如下所示:

(u'servers.uat.pkey', True)
(u'servers.uat.user', u'testval')
(u'servers.uat.pass', True)
(u'servers.dev.pkey', True)
(u'servers.dev.user', u'testval')
(u'servers.dev.pass', True)

我知道我发帖已经远远超过了被采纳答案的时间,但由于被采纳答案对我无效,也许这个更加结构独立的版本能够帮助其他人!


1
而且它确实帮助了其他人!非常感谢 :) 我找不到递归保存每个值的整个路径的正确方法。 local_path=path[:] 是关键(双关语)。 - Romain

1

不确定我是否理解您的意思,但您可能想要这样做:

def traverse(dic, path=None):
    if not path:
        path = []
    for i in dic:
        local_path = path[:].append(i)
        if i['type'] == 'folder':
            for j in traverse(i['children'], local_path):
                yield j, local_path
        elif i['type'] == 'url':
            yield i, local_path

现在您的函数返回该项及到达某个位置的项的一系列键。

我正在沿着这个方向思考。这仍需要工作,因为当i不是字典(经常发生)时,它将在i ['type']处遇到TypeError。 - azhrei
太棒了。它并不完美,但给了我一个想法。谢谢! - 比尔盖子

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接