如何将JSON数据加载到嵌套类中?

Cha*_*has 14 python json json-deserialization

我有如下JSON数据:

{
    "Address": {
        "House_Number": 2,
        "State": "MA",
        "Street_Number": 13
    },
    "Name": "John"
}
Run Code Online (Sandbox Code Playgroud)

我想将它加载到如下定义的类中:

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

class Employee:
    def __init__(self):
        self.Name = ''
        self.Address = Address()
Run Code Online (Sandbox Code Playgroud)

如果我使用类Employeeobject_hook,然后它使用两个物体的相同类(具有外部对象NameAddress作为成员和内部具有对象成员House_Number等).

基本上,如果e是加载JSON数据的对象,那么 type(e.Address)应该Address不是Employee.

有没有办法将这个JSON数据加载到Employee维护类层次结构的类中?层次结构可以任意深入.

par*_*ent 13

您可以通过查看其键来识别对象.然后,您可以将它们映射到适当的类.

使用您的示例数据:

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}
Run Code Online (Sandbox Code Playgroud)

然后,创建一个将字典转换为适当的python类的函数.这将传递给json.loadobject_hook:

def class_mapper(d):
    return mapping[frozenset(d.keys())](**d)
Run Code Online (Sandbox Code Playgroud)

之上frozenset使用是因为json中的dict键是无序的(因此是集合)并且映射中的dict键需要是可散列的(因此"冻结").

最后,使用以下class_mapper函数解析json 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
Run Code Online (Sandbox Code Playgroud)

附加

如果您的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))
Run Code Online (Sandbox Code Playgroud)