【问题标题】:Create a JSON type nested dictionary from a list in Python从 Python 中的列表创建 JSON 类型的嵌套字典
【发布时间】:2018-11-06 03:57:50
【问题描述】:

我希望从列表列表中创建一个 JSON 类型的嵌套字典。这些列表包含一个完整的目录路径,但我将它们分解为单独的组件,因为我认为这样可以更轻松地创建嵌套字典。

示例列表:

["root", "dir1", "file.txt"] 

预期结果:

{
    "type": "directory",
    "name": "root",
    "children": [
        {
            "type": "directory",
            "name": "dir1",
            "children": [
                {
                    "type": "file",
                    "name": "file.txt",
                }
            ]
        }
    ]
}      

我尝试过使用递归方法,但无法完全实现(递归方法的新手,我的脑袋不断旋转)。还从我在这里找到的一个想法(堆栈溢出)中尝试了一种迭代方法,它反转列表并向后构建字典,我有点开始工作,但无法解决其中一个解决方案要求,即代码可以在遍历列表列表时处理部分目录路径中的重复。 例如从上一个例子开始,下一个输入的列表是这样的:-

["root", "dir1", "dir2", "file2.txt"]

它需要建立在 JSON 字典上来生成这个:-

{
    "type": "directory",
    "name": "root",
    "children": [
        {
            "type": "directory",
            "name": "dir1",
            "children": [
                {
                    "type": "file",
                    "name": "file.txt",
                }
                {
                    "type": "directory",
                    "name": "dir2",
                    "children": [
                        {
                            "type": "file",
                            "name": "file2.txt"
                        }
                    ]
                }
            ]
        }
    ]
} 

以此类推,包含未知数量的包含目录路径的列表。 谢谢。

【问题讨论】:

  • 你怎么知道什么是文件,什么是目录?
  • 它们都是绝对路径吗(即从root开始)?
  • 什么是“文件夹”和什么是“文件”由它们在列表中的位置决定。列表中的最后一个值始终是一个文件,并且所有前面的字符串都与文件夹相关。它们都是绝对路径,从root开始。
  • 感谢您的解决方案,我会尽快尝试并得到结果。

标签: python json dictionary


【解决方案1】:

itertools.groupby 的递归解决方案如下(假设所有路径都是绝对路径)。这个想法是按路径列表中的第一个元素对路径进行分组。这将相似的目录根分组在一起,允许我们在该组上递归调用该函数。

还要注意,目录中的文件名不能重复,所以所有文件都将被groupby分组为单个元素列表:

from itertools import groupby
from operator import itemgetter

def build_dict(paths):
    if len(paths) == 1 and len(paths[0]) == 1:
        return {"type": "file", "name": paths[0][0]}
    dirname = paths[0][0]
    d = {"type": "directory", "name": dirname, "children": []}
    for k, g in groupby(sorted([p[1:] for p in paths], key=itemgetter(0)),
                        key=itemgetter(0)):
        d["children"].append(build_dict(list(g)))
    return d    

paths = [["root", "dir1", "file.txt"], ["root", "dir1", "dir2", "file2.txt"]]
print(build_dict(paths))

输出

{
  "type": "directory",
  "name": "root",
  "children": [
    {
      "type": "directory",
      "name": "dir1",
      "children": [
        {
          "type": "directory",
          "name": "dir2",
          "children": [
            {
              "type": "file",
              "name": "file2.txt"
            }
          ]
        },
        {
          "type": "file",
          "name": "file.txt"
        }
      ]
    }
  ]
}

【讨论】:

    【解决方案2】:

    这是一个简单的递归解决方案,它简单地遍历树结构,根据需要添加子级,直到到达 path 的最后一个元素(假设是一个文件)。

    import json
    
    
    def path_to_json(path, root):
        if path:
            curr = path.pop(0)
    
            if not root:
                root["type"] = "file"
                root["name"] = curr
    
                if path:
                    root["children"] = [{}]
                    root["type"] = "directory"
                    path_to_json(path, root["children"][0])
            elif path:
                try:
                    i = [x["name"] for x in root["children"]].index(path[0])
                    path_to_json(path, root["children"][i])
                except ValueError:        
                    root["children"].append({})
                    path_to_json(path, root["children"][-1])
    
        return root
    
    
    if __name__ == "__main__":
        paths = [["root", "dir1", "file.txt"], 
                 ["root", "dir1", "dir2", "file2.txt"]]
        result = {}
        print(json.dumps([path_to_json(x, result) for x in paths][0], indent=4))
    

    输出:

    {
        "type": "directory",
        "name": "root",
        "children": [
            {
                "type": "directory",
                "name": "dir1",
                "children": [
                    {
                        "type": "file",
                        "name": "file.txt"
                    },
                    {
                        "type": "directory",
                        "name": "dir2",
                        "children": [
                            {
                                "type": "file",
                                "name": "file2.txt"
                            }
                        ]
                    }
                ]
            }
        ]
    }
    

    Try it!

    【讨论】:

    • 感谢 ggorlen 的建议。它提供了预期的输出,但是你能告诉我为什么 path_to_json 方法在方法返回到 main 之后保留它创建的字典的内存(遵循 返回根)。它似乎就像一个生成器,但我在代码中找不到任何以这种方式定义它的东西。
    • 这实际上是我的糟糕设计。第一次之后的每个调用都使用与原始默认参数相同的{}。已更新。
    【解决方案3】:

    由于没有提供太多细节,这里提供一个解决方案,使用引用输入每个嵌套的dict

    In [537]: structure = ["root", "dir1", "dir2", "file2.txt"]
    
    In [538]: d = {}
    
    # Create a reference to the current dict
    In [541]: curr = d
    
    In [542]: for i, s in enumerate(structure):
         ...:     curr['name'] = s
         ...:     if i != len(structure) - 1:
         ...:         curr['type'] = 'directory'
         ...:         curr['children'] = {}
         ...:         curr = curr['children']          # New reference is the child dict
         ...:     else:
         ...:         curr['type'] = 'file'
         ...:
    
    In [544]: from pprint import pprint
    
    In [545]: pprint(d)
    {'children': {'children': {'children': {'name': 'file2.txt', 'type': 'file'},
                               'name': 'dir2',
                               'type': 'directory'},
                  'name': 'dir1',
                  'type': 'directory'},
     'name': 'root',
     'type': 'directory'}
    

    我不知道这是否适用于您的所有问题,因为规范不是很详细

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多