任意字典(JSON)转为严格树状字典(JSON)在Python中

2 投票
1 回答
1090 浏览
提问于 2025-04-17 14:59

大家好,

我有一些字典,这些字典里面可能包含任意数量的其他字典和数组,结构可能是这样的:

{
    "a": {
        "b": "1",
        "c": [
            {
                "d": "2"
            },
            {
                "d": {
                    "e": "3",
                    "f": "4"
                }
            }
        ]
    }
}

我想把这些数据以树的形式展示出来,为了做到这一点,我想把它转换成这样的结构:

{
    "name": "a",
    "children": [
        {
            "name": "b",
            "value": "1"
        },
        {
            "name": "c",
            "children": [
                {
                    "name": "d",
                    "value": "2"
                },
                {
                    "name": "d",
                    "children": [
                        {
                            "name": "e",
                            "value": "3"
                        },
                        {
                            "name": "f",
                            "value": "4"
                        }
                    ]
                }
            ]
        }
    ]
}

为了实现这个目标,我觉得需要把子字典和子数组处理得差不多。不过,我在想出一个方法来进行这种转换时遇到了很大的困难。我尝试过使用递归的方法,也试过创建一个节点类,这个类包含“名称”、“值”和“子节点”等信息,但我在解析一个深度不确定的输入,以创建这种常规的节点/树结构时遇到了麻烦。

这听起来像是大家之前做过的事情吗?希望能听到你们的意见。

1 个回答

4

你很聪明,正确地把这个结构看作树形数据结构,而且你决定转换它也是明智的,因为原来的表示方式显然有问题。

现在你知道这是一个树,你需要想想它的节点是什么。在你的例子中,节点可以是:

  1. 一个叶子节点,它有一个名字和一个值
  2. 一个内部节点,它有一个名字和一些子节点

问题是,第二种类型的节点有两种不同的表示方式:

  1. 一种是 ("<name>", { "<child1>" : ..., "<child2>" })
  2. 另一种是 ("<name>", [{"<child1>" : ... }, { "<child2>": ... }])

你的例子没有展示,但也许下面的形式也是可以的:

("<name>", [{"<child1>" : ... }, { "<child2>": ..., "<child3>": ... }])

使用递归确实是解决这个问题的好方法。最简单的情况就是叶子节点。你只需要先检查另外两种情况:

def transform_node(name, val):
    if isinstance(val, list):
        val = ("children", [transform_node(k,v) for x in val for k, v in x.items()])
    elif isinstance(val, dict):
        val = ("children", [transform_node(*kv) for kv in val.items()])
    else:
        val = ("value", val)
    return dict([("name", name), val])

现在你得到了:

>>> transform_node("a", 3)
{'name': 'a', 'value': 3}
>>> transform_node("a", { "c1" : 3, "c2" : 4 })
{'name': 'a', 'children': [{'name': 'c2', 'value': 4}, {'name': 'c1', 'value': 3}]}
>>> transform_node("a", [{ "c1" : 3 }, { "c2" : 4, "c3": 5 }])
{'name': 'a', 'children': [{'name': 'c1', 'value': 3}, {'name': 'c3', 'value': 5}, {'name': 'c2', 'value': 4}]}

太好了!现在你可以直接这样做:

>>> tree = { "a": { "b": 1 }}
>>> transform_node("root", tree)
{'name': 'root', 'children': [{'name': 'a', 'children': [{'name': 'b', 'value': 1}]}]}

当然,如果你想让“a”成为根节点,你只需要提取根节点的第一个子节点。

不过,可能更好的是用类来表示这个结构,而不是用字典,因为字典是完全没有类型的。

撰写回答