Подтвердить что ты не робот

Как загрузить данные JSON в вложенные классы?

У меня есть данные JSON следующим образом:

{
    "Address": {
        "House_Number": 2,
        "State": "MA",
        "Street_Number": 13
    },
    "Name": "John"
}

Я хочу загрузить его в класс, определенный следующим образом:

class Address:
    def __init__(self):
        self.House_Number = 0

class Employee:
    def __init__(self):
        self.Name = ''
        self.Address = Address()

Если я использую класс Employee как object_hook, то он использует тот же класс для обоих объектов (внешний объект имеет Name и Address как члены и внутренний объект, имеющий членов House_Number и т.д.).

В принципе, если e - это объект, в который были загружены данные JSON, тогда type(e.Address) должен быть Address не Employee.

Есть ли способ загрузить данные JSON в класс Employee, поддерживающий иерархию классов? Иерархия может быть сколь угодно глубокой.

4b9b3361

Ответ 1

Вы можете идентифицировать объекты, просматривая их ключи. Затем вы можете сопоставить их с соответствующим классом.

Используя данные примера:

class AddressClass:
    # The parameters to init needs to be the same as the json keys
    def __init__(self, House_Number, Street_Number, State):
        self.house_number = House_Number
        self.street_number = Street_Number
        self.state = State

class EmployeeClass:
    # Same here
    def __init__(self, Name, Address):
        self.name = Name
        self.address = Address

# Map keys to classes
mapping = {frozenset(('House_Number', 
                      'Street_Number', 
                      'State')): AddressClass,
           frozenset(('Name', 
                      'Address')): EmployeeClass}

Затем создайте функцию, которая преобразует словарь в соответствующий класс python. Это будет передано json.load как object_hook:

def class_mapper(d):
    return mapping[frozenset(d.keys())](**d)

Выше, frozenset используется, потому что ключи dict в json неупорядочены (следовательно, набор), а клавиши dict в сопоставлении должны быть хешируемыми (следовательно, "замороженными" ).

Наконец, проанализируйте json с помощью функции class_mapper как object_hook:

j = '''
{
    "Address": {
        "House_Number": 2,
        "State": "MA",
        "Street_Number": 13
    },
    "Name": "John"
}
'''
employee = json.loads(j, object_hook=class_mapper)
print(employee.name,
      employee.address.house_number,
      employee.address.street_number,
      employee.address.state)
# John 2 13 MA

Дополнения

Если ваши json-данные имеют необязательные ключи, вы можете создать более надежный class_mapper, хотя он может быть немного медленнее. Вам также необходимо добавить значения по умолчанию к параметрам конструктора классов, которые являются необязательными.

def class_mapper(d):
    for keys, cls in mapping.items():
        if keys.issuperset(d.keys()):
            return cls(**d)
    else:
        # Raise exception instead of silently returning None
        raise ValueError('Unable to find a matching class for object: {!s}'.format(d))