【问题标题】:Flatten JSON / Dictionaries / List展平 JSON / 字典 / 列表
【发布时间】:2021-01-05 13:21:41
【问题描述】:

我有一个嵌套的 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 中,而不必无限增加列数,而是增加行数。

【问题讨论】:

  • 您的代码缩进不正确。请解决这个问题。另外,请发布您现在得到的内容,并通过创建minimal reproducible example 缩小错误来源
  • 在 Pranav 的指导下编辑了代码
  • @nomad 请指定您想要的输出。由于无法推断 想要的结构,因此图像不计入...您希望它是列表列表吗?
  • 您是想总体上展平,还是使用输入的领域知识? zip(详细信息,值)呢?这看起来像是领域知识,尽管我们可以自动压缩最外层列表下方的任何列表。

标签: python json nested flatten


【解决方案1】:

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

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

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

【讨论】:

  • 谢谢,我可以暂时试一试。但是这个等待我必须在我的 json 中总是有相同的键和相同的嵌套列表。就我而言,它可能会有所不同。
  • 我正在尝试使用此处提供的功能。我仍然有一些问题,但这与我需要的非常接近。 stackoverflow.com/questions/51359783/…stackoverflow.com/questions/51359783/…
  • 你能描述一下它到底有什么不同吗?
  • 我可以拥有另一个具有嵌套值的键,就像 details 键一样。我仍然可以使用您的代码使其工作,但每次我都会有一个新的嵌套键,我可以手动更新代码
【解决方案2】:

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

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

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

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

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 条详细信息记录和 3 条其他记录,我不得不怀疑您的预期输出是错误的。我能理解的唯一解析规则是同步推进所有列表,因此放弃额外的时间和值测量。如果你能帮助我理解解析规则,以便我理解你是如何得到四行的,我们可以重新审视这个。我得到了:

{'Details.ID': 0,
'Details.Name': 'TEST1',
'Details.Value': 'XXXXXX',
'ID':300,
“时间”:1600358400,
“价值观”:0}

{'Details.ID': 1,
'Details.Name': 'TEST2',
'Details.Value': 'DDDDDDDD',
'ID':300,
“时间”:1600358700,
“价值观”:0}

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

【讨论】:

  • 如果正确的输出应该有四行,请告诉我,我可以更新或删除这个答案。我不明白你是怎么得到的。如果它是几个表的外连接,也许我们应该单独解析这些表。
  • 既然我们有多个“时间”,我们应该有同样多的结果。但我理解你的想法。
猜你喜欢
  • 2018-07-19
  • 2019-11-04
  • 2019-01-11
  • 2020-12-24
  • 2014-02-26
  • 2021-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多