Как извлечь поля из вложенного json и сохранить в структуре данных

#python #json #data-structures #nested

Вопрос:

Используя python, я пытаюсь извлечь поля с именами «первый», «последний» и «почтовый индекс» и их соответствующие значения из JSON, где структура не всегда известна. Пример JSON может выглядеть примерно так:

 {
"employees": [
    {
        "first": "Alice",
        "last_name": "Alast",
        "zipcode": "12345",
        "role": "dev",
        "nbr": 1,
        "team": [
            {
                "first_name": "fn",
                "last_name": "ln"
            },
            {
                "first_name": "fn2",
                "last_name": "ln2"
            }
        ]
    },
    {
        "name": "Bob",
        "role": "dev",
        "nbr": 2
    }
],
"firm": {
    "last_name": "Lhans",
    "zipcode": "67890",
    "location": "CA"
}}
 

В дополнение к этому я хочу сохранить это в структуре данных, такой как:

 { 
  {
    first: "firstname",
    last: "lastname",
    zipcode: "zipcode"
  }
}
 

Я попытался сгладить вложенный JSON, основываясь на этой функции. Я могу получить поля таким образом, но мне трудно найти оптимальный способ сохранения этих данных в формате модели, упомянутой выше. Если одно из полей пусто, я хочу заполнить это поле как NaN или пустую строку, а не полностью игнорировать его. Вот что у меня есть до сих пор, которое создает список полей и значений, но если поле не существует, оно пропускает его вместо заполнения значением «нет».

 def flatten_json(nested_json, fields: list):
    out = []
    
    def flatten(x, name=''):
            if type(x) is dict:
                for a in x:
                    flatten(x[a], a)
            elif type(x) is list:
                i = 0
                for a in x:
                    flatten(a)
                    i  = 1
            elif name in fields:
                out.append(name ": " x)
    flatten(nested_json)
    return out
 

Это дает мне что-то вроде:

 ['first: Alice', 'last: Jones', 'zipcode: 12345', 'first: fn1', 'last: ln1', 'first: fn2', 'last: ln2', 'last: ln3', 'zipcode: 67890']
 

Что не идеально. Я бы предпочел, чтобы все отсутствующие поля были заполнены NaN или пустой строкой, а не отсутствовали в списке.

Комментарии:

1. Как выглядит JSON, который вы пытаетесь «сгладить»?

2. @ScottHunter только что обновил мой вопрос примером JSON. Но это не всегда будет такой точный формат, и кардинальность расположения полей может измениться.

Ответ №1:

Я изменил вашу функцию, чтобы захватить список словарей. Словарь будет содержать только поля, указанные в списке полей в качестве ключей.

 
import pandas as pd


def flatten_json(nested_json, fields):
    out = []
    temp = {}

    def flatten(x, name=''):
        nonlocal temp
        if type(x) is dict:
            temp = {}
            for a in x:
                flatten(x[a], a)
        elif type(x) is list:
            for i, a in enumerate(x):
                flatten(a)
                i  = 1
        elif name in fields:
            temp[name] = x
            out.append(temp)
    flatten(nested_json)
    return out


json1 = {"employees": [{"first": "Alice", "last_name": "Alast", "zipcode": "12345", "role": "dev", "nbr": 1, "team": [{"first_name": "fn", "last_name": "ln"}, {
    "first_name": "fn2", "last_name": "ln2"}]}, {"name": "Bob", "role": "dev", "nbr": 2}], "firm": {"last_name": "Lhans", "zipcode": "67890", "location": "CA"}}

fields = ['first_name', 'last_name', 'zipcode']
result = (flatten_json(json1, fields))
 

Выходные данные вышеуказанной функции затем могут быть загружены в фрейм данных pandas —

 df = pd.DataFrame(result)
df.drop_duplicates(inplace=True)
print(df)
 

что даст такой результат —

   last_name zipcode first_name
0     Alast   12345        NaN
2        ln     NaN         fn
4       ln2     NaN        fn2
6     Lhans   67890        NaN
 

Теперь, чтобы получить данные обратно в формате JSON, вы можете преобразовать фрейм данных обратно в dict с помощью функции to_dict() —

 print(df.to_dict(orient='records'))
 

выход-

 [{'first_name': nan, 'last_name': 'Alast', 'zipcode': '12345'},
 {'first_name': 'fn', 'last_name': 'ln', 'zipcode': nan},
 {'first_name': 'fn2', 'last_name': 'ln2', 'zipcode': nan},
 {'first_name': nan, 'last_name': 'Lhans', 'zipcode': '67890'}]
 

Комментарии:

1. Спасибо, это именно то, чего я пытался достичь.