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

时间:2014-11-20 11:18:19

标签: python json json-deserialization

我有以下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,那么它对两个对象使用相同的类(具有NameAddress的外部对象作为成员和内部具有成员House_Number等的对象。)。

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

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

1 个答案:

答案 0 :(得分: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}

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

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

在上面,使用了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

附加

如果你的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))