从Python中的列表创建JSON类型的嵌套字典

时间:2018-11-06 03:57:50

标签: python json dictionary

我希望从列表列表中创建一个JSON类型的嵌套字典。列表包含完整的目录路径,但是我将它们分解为各自的组件,因为我认为这样会使嵌套字典的创建更加容易。

示例列表:

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

预期结果:

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

我尝试使用递归方法,但无法完全到达递归方法(递归方法的新手,我的头脑不断旋转)。我还尝试了一种迭代方法,该方法是我在这里找到的一个想法(堆栈溢出),它可以颠倒列表并向后构建dict,虽然我可以使用,但无法解决其中一个解决方案要求,即代码可以遍历列表列表时处理部分目录路径中的重复项。 例如,从上一个示例开始,下一个输入的列表是:-

["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"
                        }
                    ]
                }
            ]
        }
    ]
} 

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

3 个答案:

答案 0 :(得分:1)

这是一个幼稚的递归解决方案,它简单地遍历树结构,并根据需要添加子项,直到到达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!

答案 1 :(得分: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 :(得分:0)

鉴于没有提供太多细节,这是一个使用引用输入每个嵌套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'}

我不知道这是否可以解决您的所有问题,因为规格不是很详细