import csv
import sys
import typing
import json
def deep_walk(j, path=()):
if isinstance(j, dict):
for k, v in j.items():
yield from deep_walk(v, path + (f'.{k}',))
elif isinstance(j, list):
for i, v in enumerate(j):
yield from deep_walk(v, path + (f'[{i}]',))
else:
yield path, j
def json2csv(data: typing.Union[dict, list], dest: typing.TextIO = None):
field_set = set()
records = []
if isinstance(data, dict):
data = [data]
for item in data:
record = {''.join(path).lstrip('.'): value for path, value in deep_walk(item)}
records.append(record)
field_set.update(record.keys())
w = csv.DictWriter(dest or sys.stdout, fieldnames=list(sorted(field_set)))
w.writeheader()
w.writerows(records)
if __name__ == '__main__':
if sys.argv[1:]:
with open(sys.argv[1]) as f:
json2csv(json.load(f))
else:
json2csv(json.load(sys.stdin))
Входной json можно передать в виде имени файла первым параметром или отправить его программе через stdin.
Запостил чтобы показать, что такие вещи - не "рокет сайнс". Просто нужно чуточку подумать и сделать.
А вот как сделать, чтобы прога не нуждалась в загрузке всего входного json-а в память, прежде чем начать писать в выхлоп - это уже куда интереснее.
Например можно наставить в начале файла много-премного пробелов (чтоб наверняка хватило на заголовок), а потом по факту вывода всего файла дорисовать в начало заголовок в нужном порядке. Но это так себе идеечка. Костылями попахивает.