繁体   English   中英

扁平化 JSON / 字典 / 列表

[英]Flatten JSON / Dictionaries / List

我有一个嵌套的 JSON

{
    "ID": 300,
    "Name": " TEST",
    "Value": [
        {
            "Details": [
                {
                    "Name": "TEST1",
                    "Value": "XXXXXX"
                },
                {
                    "Name": "TEST2",
                    "Value": "DDDDDDDD"
                }
            ],
            "Time": [ 1600358400, 1600358700, 1600359000],
            "Values": [ 0, 0, 0]
        }
    ]
}

我想展平 json 以便能够得到一个列表

在此处输入图片说明

我使用了 itertools groupby 但无法达到预期的结果。 它是水平展平的。

到目前为止我尝试过的代码

from itertools import groupby
import json

def myflatten(d, depth=0):
    rv = [({}, depth)]
    if isinstance(d, dict):
        for k, v in d.items():
            if not isinstance(v, dict) and not isinstance(v, list):
                for i in rv:
                    i[0][k] = v
            else:
                for (vv, _depth) in myflatten(v,depth+1):
                    rv.append((rv[-1][0].copy(), _depth))
                    for kkk, vvv in vv.items():
                        rv[-1][0][kkk] = vvv
    elif isinstance(d, list):
        for v in d:
            rv.append((rv[-1][0].copy(), depth+1))
            for (vv, _) in myflatten(v,depth+1):
                for kkk, vvv in vv.items():
                    rv[-1][0][kkk] = vvv
    for i, _depth in rv:
        yield i, _depth

out = []

a = {
    "ID": 300,
    "Name": " TEST",
    "Value": [
        {
            "Details": [
                {
                    "Name": "TEST1",
                    "Value": "XXXXXX"
                },
                {
                    "Name": "TEST2",
                    "Value": "DDDDDDDD"
                }
            ],
            "Time": [ 1600358400, 1600358700, 1600359000],
            "Values": [ 0, 0, 0]
        }
    ]
}

for v, g in groupby(sorted(myflatten(a), key=lambda k: -k[1]), lambda k: k[1]):
    out.extend(i[0] for i in g)
    break
print(out)

有人可以帮助垂直而不是水平展平嵌套的 json/dict/list 吗? 最终目标是能够将数据存储在 RDBMS 中,而不必无限增加列数而是行数。

由于在您的情况下,您需要将行(或记录)存储在数据库中,因此您可以使用一个函数来生成字典列表,您可以迭代地将其添加到数据库中。 此外,由于您的代码似乎过于嵌套,您可以考虑以下代码片段:

def flatten(dictionary):
    my_list = []
    _id = dictionary["id"]
    name = dictionary["Name"]

    for obj in dictionary["Value"]:
        details = obj["Details"]
        time = obj["Time"]
        vals = obj["Values"]

        for i in range(len(time)):
            for (index, detail_obj) in enumerate(details):
                my_list.append({
                    "ID": _id,
                    "Name": name,
                    "Details.ID": index,
                    "Details.Name": detail_obj["Name"]
                    "Details.Value": detail_obj["Value"],
                    "Time.ID": i,
                    "Time.Time": time[i],
                    "Time.Value": vals[i]
                })

    return my_list

注意:此函数将一次处理一个嵌套字典(如您在问题中提供的字典)。 因此,如果您有多个这样的嵌套字典,您可能希望为这些嵌套字典中的每一个调用此函数。

为了概括这一点,我们需要展平所有内容,但将迭代器返回到列表。 然后我们将这些迭代器压缩在一起,因此它们都以相同的速度前进。 但是这些迭代器可能是 dicts,它们本身必须被扁平化,记住它们来自的层次结构。

此外,请注意,外部字典的 ID、名称、值不受所有这些限制,并且不包含在递归命名方案中,因此我们将编写一个顶级处理程序来解析它们,然后设置其余部分。 并且“Details.ID”不是数据的一部分,因此我对其进行了硬编码,其 ID 取自上述列表 zip 上的 enumerate。

我承认,概括 flatten 函数来处理字典和列表以及标量数据的任意嵌套对我来说有点太多了,因为我们需要链接迭代器并为几种不同类型的输入选择一致的返回类型。 太多了 ...

相反,我在有限的范围内使用数据的结构,并假设只有一个级别有列表。 我只在数据中展平。 这大大简化了问题,同时仍然接受所有级别的任意名称和值:

import itertools, json, typing, pprint

# top level function starts recursion
def parse(data):
    fixed_fields = {k:v for k,v in data.items() if not isinstance(v, typing.Iterable)}
    for testcase in data['Value']:
        for testcase in data['Value']:
            for record in parse_testcase(testcase):
                record.update(fixed_fields)
                yield record

def parse_testcase(testcase):
    names = []
    values = []
    for key, value in testcase.items():
        names.append(key)
        values.append(itertools.chain(value))
    
    for details_id, row in enumerate(zip(*values)):
        record = {'Details.ID': details_id}
        for name, value in zip(names, row):
            if isinstance(value, dict):
                flatten(name, value, record)
            else:
                record[name] = value
        yield record
        
def flatten(parent_key, details, result):
    for key, value in details.items():
        keyname = get_keyname(parent_key, key)
        if isinstance(value, dict):
            flatten(keyname, value, result)
        else:
            result[keyname] = value

def get_keyname(parent_key, key):
    if parent_key:
        return '.'.join((parent_key, key))
    return key

text = """{
    "ID": 300,
    "Name": " TEST",
    "Value": [
        {
            "Details": [
                {
                    "Name": "TEST1",
                    "Value": "XXXXXX"
                },
                {
                    "Name": "TEST2",
                    "Value": "DDDDDDDD"
                }
            ],
            "Time": [ 1600358400, 1600358700, 1600359000],
            "Values": [ 0, 0, 0]
        }
    ]
}"""

for record in parse(json.loads(text)):
    pprint.pprint(record)

由于您有 2 个 Details 记录和 3 个其他记录,我不得不怀疑您的预期输出是错误的。 我能理解的唯一解析规则是同步推进所有列表,因此删除额外的时间和值测量。 如果你能帮助我理解解析规则,让我理解你是如何得到四行的,我们可以重新审视这个。 我有:

{'详细信息.ID':0,
'Details.Name': 'TEST1',
'详细信息。价值':'XXXXXX',
'ID':300,
'时间':1600358400,
“值”:0}

{'详细信息.ID':1,
'Details.Name': 'TEST2',
'Details.Value': 'DDDDDDDD',
'ID':300,
'时间':1600358700,
“值”:0}

我还注意到您的输出具有由输入决定的任意方案。 也许输出一个 csv 文件是明智的,并将其放入关系数据库是一项单独的任务。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM