如何将 JSON 数据转换为树形图像?

新手上路,请多包涵

我正在使用 treelib 生成树,现在我需要易于阅读的树版本,所以我想将它们转换成图像。例如: 在此处输入图像描述

以下树的示例 JSON 数据:

在此处输入图像描述

有数据:

 >>> print(tree.to_json(with_data=True))
{"Harry": {"data": null, "children": [{"Bill": {"data": null}}, {"Jane": {"data": null, "children": [{"Diane": {"data": null}}, {"Mark": {"data": null}}]}}, {"Mary": {"data": null}}]}}

没有数据:

 >>> print(tree.to_json(with_data=False))
{"Harry": {"children": ["Bill", {"Jane": {"children": [{"Diane": {"children": ["Mary"]}}, "Mark"]}}]}}

无论如何使用 graphvizd3.js 或其他一些 python 库来使用此 JSON 数据生成树?

原文由 Grimlock 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.8k
2 个回答

对于这样的树,无需使用库:您可以直接生成 Graphviz DOT 语言语句。唯一棘手的部分是从 JSON 数据中提取树的边缘。为此,我们首先将 JSON 字符串转换回 Python dict ,然后递归解析 dict

如果树字典中的名称没有子项,则它是一个简单的字符串,否则,它是一个字典,我们需要扫描其 "children" 列表中的项目。我们找到的每个(父母,孩子)对都附加到全局列表 edges

这个有点神秘的行:

 name = next(iter(treedict.keys()))

treedict 获取单个密钥。这给了我们这个人的名字,因为这是 treedict 中唯一的关键。在 Python 2 中我们可以做

name = treedict.keys()[0]

但是前面的代码在 Python 2 和 Python 3 中都有效。

 from __future__ import print_function
import json
import sys

# Tree in JSON format
s = '{"Harry": {"children": ["Bill", {"Jane": {"children": [{"Diane": {"children": ["Mary"]}}, "Mark"]}}]}}'

# Convert JSON tree to a Python dict
data = json.loads(s)

# Convert back to JSON & print to stderr so we can verify that the tree is correct.
print(json.dumps(data, indent=4), file=sys.stderr)

# Extract tree edges from the dict
edges = []

def get_edges(treedict, parent=None):
    name = next(iter(treedict.keys()))
    if parent is not None:
        edges.append((parent, name))
    for item in treedict[name]["children"]:
        if isinstance(item, dict):
            get_edges(item, parent=name)
        else:
            edges.append((name, item))

get_edges(data)

# Dump edge list in Graphviz DOT format
print('strict digraph tree {')
for row in edges:
    print('    {0} -> {1};'.format(*row))
print('}')

标准错误输出

{
    "Harry": {
        "children": [
            "Bill",
            {
                "Jane": {
                    "children": [
                        {
                            "Diane": {
                                "children": [
                                    "Mary"
                                ]
                            }
                        },
                        "Mark"
                    ]
                }
            }
        ]
    }
}

标准输出

strict digraph tree {
    Harry -> Bill;
    Harry -> Jane;
    Jane -> Diane;
    Diane -> Mary;
    Jane -> Mark;
}

上面的代码在 Python 2 和 Python 3 上运行。它将 JSON 数据打印到 stderr,以便我们验证它是否正确。然后它将 Graphviz 数据打印到标准输出,以便我们可以将其捕获到文件或直接通过管道传输到 Graphviz 程序。例如,如果脚本名为“tree_to_graph.py”,那么您可以在命令行中执行此操作以将图形保存为名为“tree.png”的 PNG 文件:

 python tree_to_graph.py | dot -Tpng -otree.png

这是 PNG 输出:

Tree made by Graphviz

原文由 PM 2Ring 发布,翻译遵循 CC BY-SA 4.0 许可协议

根据 PM 2Ring 的回答,我创建了一个可以通过命令行使用的脚本:

 #!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Convert a JSON to a graph."""

from __future__ import print_function
import json
import sys

def tree2graph(data, verbose=True):
    """
    Convert a JSON to a graph.

    Run `dot -Tpng -otree.png`

    Parameters
    ----------
    json_filepath : str
        Path to a JSON file
    out_dot_path : str
        Path where the output dot file will be stored

    Examples
    --------
    >>> s = {"Harry": [ "Bill", \
                       {"Jane": [{"Diane": ["Mary", "Mark"]}]}]}
    >>> tree2graph(s)
    [('Harry', 'Bill'), ('Harry', 'Jane'), ('Jane', 'Diane'), ('Diane', 'Mary'), ('Diane', 'Mark')]
    """
    # Extract tree edges from the dict
    edges = []

    def get_edges(treedict, parent=None):
        name = next(iter(treedict.keys()))
        if parent is not None:
            edges.append((parent, name))
        for item in treedict[name]:
            if isinstance(item, dict):
                get_edges(item, parent=name)
            elif isinstance(item, list):
                for el in item:
                    if isinstance(item, dict):
                        edges.append((parent, item.keys()[0]))
                        get_edges(item[item.keys()[0]])
                    else:
                        edges.append((parent, el))
            else:
                edges.append((name, item))
    get_edges(data)
    return edges

def main(json_filepath, out_dot_path, lr=False, verbose=True):
    """IO."""
    # Read JSON
    with open(json_filepath) as data_file:
        data = json.load(data_file)

    if verbose:
        # Convert back to JSON & print to stderr so we can verfiy that the tree
        # is correct.
        print(json.dumps(data, indent=4), file=sys.stderr)

    # Get edges
    edges = tree2graph(data, verbose)

    # Dump edge list in Graphviz DOT format
    with open(out_dot_path, 'w') as f:
        f.write('strict digraph tree {\n')
        if lr:
            f.write('rankdir="LR";\n')
        for row in edges:
            f.write('    "{0}" -> "{1}";\n'.format(*row))
        f.write('}\n')

def get_parser():
    """Get parser object for tree2graph.py."""
    from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
    parser = ArgumentParser(description=__doc__,
                            formatter_class=ArgumentDefaultsHelpFormatter)
    parser.add_argument("-i", "--input",
                        dest="json_filepath",
                        help="JSON FILE to read",
                        metavar="FILE",
                        required=True)
    parser.add_argument("-o", "--output",
                        dest="out_dot_path",
                        help="DOT FILE to write",
                        metavar="FILE",
                        required=True)
    return parser

if __name__ == "__main__":
    import doctest
    doctest.testmod()
    args = get_parser().parse_args()
    main(args.json_filepath, args.out_dot_path, verbose=False)

原文由 Martin Thoma 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题