Условие
API возвращает массив JSON-объектов:
[
{"order_id": 1, "user": {"id": 42, "city": "Moscow"},
"items": [{"sku": "A", "qty": 2}, {"sku": "B", "qty": 1}]},
...
]Нужно получить плоскую таблицу order_id, user_id, city, sku, qty.
Решение
json_normalize
import pandas as pd
# meta — поля сверху, record_path — путь до массива
flat = pd.json_normalize(
data,
record_path='items',
meta=['order_id', ['user', 'id'], ['user', 'city']],
record_prefix='item_',
)
flat = flat.rename(columns={
'user.id': 'user_id',
'user.city': 'city',
'item_sku': 'sku',
'item_qty': 'qty',
})Если массивы лежат глубже
# data = {"orders": [...]}
pd.json_normalize(data, record_path=['orders', 'items'], meta=[['orders', 'order_id']])Список нерегулярных JSON
Если у некоторых записей нет items — record_path падает. Решение: errors='ignore' или пред-фильтрация.
data_valid = [d for d in data if 'items' in d and d['items']]
flat = pd.json_normalize(data_valid, record_path='items', meta=['order_id'])Подводные камни
metaпринимает либо строку ('order_id'), либо список путей (['user', 'id']). Перепутать = тихий KeyError.- По умолчанию вложенные ключи становятся
user.id— точка в имени мешает SQL/df.user.idне работает. Переименовывать. - Если в
itemsвстречаются пустые массивы, json_normalize пропускает запись — заказ исчезнет из результата. Решение: предварительно подставить[{}]. - Большие JSON (>1 ГБ) лучше парсить потоково через
ijson, а не загружать целиком. - Для глубоких структур (3+ уровня) часто проще написать руками генератор словарей и
pd.DataFrame(list).
Эталонный ответ
pd.json_normalize(data, record_path='items', meta=['order_id', ['user','id']]) для развёртки одного уровня массивов и переноса родительских полей.