首页 > 解决方案 > 将嵌套的 json 转换为 csv,其中每一行包含最内层的值和所有父值

问题描述

我正在寻找一个 python 脚本,以便能够将嵌套的 json 文件转换为 csv 文件,每个最里面的孩子都有自己的行,其中也包括行中的所有父字段。

我的嵌套 json 看起来:

(注意这只是一小段摘录,有数百个日期/值对)

{

"test1": true,
"test2": [
    {
        "name_id": 12345,
        "tags": [
            {
                "prod_id": 54321,
                "history": [
                    {
                        "date": "Feb-2-2019",
                        "value": 6
                    },
                    {
                        "date": "Feb-3-2019",
                        "value": 5
                    },
                    {
                        "date": "Feb-4-2019",
                        "value": 4
                    }

目标是写入 csv,其中每行显示最内层字段及其所有父项的值。(例如, date, value, prod_id, name_id, test1)。基本上为每个日期和值创建一行,包括所有父字段值。

我开始使用这个资源作为基础,但仍然不是我想要完成的:

如何在非递归优雅 Python 中展平深度嵌套的 JSON 对象

我尝试过调整这个脚本,但无法提出解决方案。这似乎是一项相对容易的任务,所以也许我缺少一些东西。

标签: pythonjsoncsv

解决方案


您想做的很多事情都是特定于数据格式的。这是使用从您引用的链接资源中显示的“传统递归”解决方案松散派生的函数的东西,因为它可以很好地处理这些数据,因为它没有那么深嵌套并且比迭代方法也说明了更简单。

flatten_json()函数返回一个列表,每个值对应于传递给它的 JSON 对象中的键。

请注意,这是 Python 3 代码。

from collections import OrderedDict
import csv
import json

def flatten_json(nested_json):
    """ Flatten values of JSON object dictionary. """
    name, out = [], []

    def flatten(obj):
        if isinstance(obj, dict):
            for key, value in obj.items():
                name.append(key)
                flatten(value)
        elif isinstance(obj, list):
            for index, item in enumerate(obj):
                name.append(str(index))
                flatten(item)
        else:
            out.append(obj)

    flatten(nested_json)
    return out

def grouper(iterable, n):
    """ Collect data in iterable into fixed-length chunks or blocks. """
    args = [iter(iterable)] * n
    return zip(*args)


if __name__ == '__main__':

    json_str = """
        {"test1": true,
         "test2": [
             {"name_id": 12345,
              "tags": [
                    {"prod_id": 54321,
                     "history": [
                        {"date": "Feb-2-2019", "item": 6},
                        {"date": "Feb-3-2019", "item": 5},
                        {"date": "Feb-4-2019", "item": 4}
                     ]
                    }
              ]
             }
         ]
        }
    """

    # Flatten the json object into a list of values.
    json_obj = json.loads(json_str, object_pairs_hook=OrderedDict)
    flattened = flatten_json(json_obj)
    print('flattened:', flattened)

    # Create row dictionaies for each (data, value) pair at the end of the list
    # flattened values with all of the preceeding fields repeated in each one.
    test1, name_id, prod_id = flattened[:3]
    rows = []
    for date, value in grouper(flattened[3:], 2):
        rows.append({'date': date, 'value': value,
                     'prod_id': prod_id, 'name_id': name_id, 'test1': prod_id})

    # Write rows to a csv file.
    filename = 'product_tests.csv'
    fieldnames = 'date', 'value', 'prod_id', 'name_id', 'test1'
    with open(filename, mode='w', newline='') as csvfile:
        writer = csv.DictWriter(csvfile, fieldnames)
        writer.writeheader()  # Write csv file header row (optional).
        writer.writerows(rows)

    print('"{}" file written.'.format(filename))

这是它打印的内容:

flattened: [True, 12345, 54321, 'Feb-2-2019', 6, 'Feb-3-2019', 5, 'Feb-4-2019', 4]
"product_tests.csv" file written.

这是product_tests.csv生成的文件的内容:

date,value,prod_id,name_id,test1
Feb-2-2019,6,54321,12345,True
Feb-3-2019,5,54321,12345,True
Feb-4-2019,4,54321,12345,True

推荐阅读