Python 和 JSON:使用 `object_hook` 为多个不同的类进行类提示

98 阅读2分钟

在使用 Python 2.6 中的 json 模块时,当将函数传递给 object_hook 时,可能会遇到一些意外的行为。正在尝试将 JSON 对象转换为代码中定义的类。此类需要将另一个类的实例作为参数。例如:

class OuterClass:
    def __init__(self, arg, *InnerClass_instances):
        self.arg = arg
        self.class2 = InnerClass_instances

class InnerClass:
    def __init__(self, name, value):
        self.name = name
        self.value = value

似乎,如果要创建一个 JSON 对象,其中包含 OuterClass 实例的所有信息,则需要包含将传递给它的每个 InnerClass 实例的所有信息。因此,可以编写两个函数来解析 JSON 对象,一个用于每个类,并让创建 OuterClass 的函数调用创建 InnerClass 实例的函数。例如:

def create_InnerClass(dct):
    if '__InnerClass__' in dct:
        name = dct['name']
        value = dct['value']
        return InnerClass(name, value)

def create_OuterClass(dct):
    if '__OuterClass__' in dct:
        arg = dct['arg']
        InnerClass_instances = [create_InnerClass(dct2) for dct2 in dct['InnerClass_instances']]
        return OuterClass(arg, *InnerClass_intances)

假设这对以下 JSON 对象有效:

{
"__OuterClass__": true,
"arg": 4,
"InnerClass_instances":
    [
    {"__InnerClass__": true, "name": "Joe", "value": 12},
    {"__InnerClass__": true, "name": "Jimmy", "value":7}
    ]
}

然后,使用以下代码加载 JSON 对象:

outerClass = json.loads(s, object_hook = creat_OuterClass)

但是,这样调用 json.loads 会返回以下错误:

if '__InnerClass__' in dct:
TypeError: argument of type 'NoneType' is not iterable

但是,只调用 json.loads(s),然后调用 json.dumps(outerClass) 可以正常工作。因此,似乎以这种方式使用 object_hook 参数导致定义 InnerClass 实例的词典被转换为 NoneType 对象。是否对 object_hook 函数在解析 JSON 对象中的用法存在误解?是否有一种方法可以对单个 JSON 对象中的多种类型的类进行类提示?到目前为止读到的内容都没有表明这是否可能。

2、解决方案

问题在于 create_OuterClass 函数并未正确处理不是 OuterClass 实例的词典。object_hook 函数将首先应用于每个内部词典,然后应用于外部词典。如果内部词典不是 InnerClass 实例,则 create_InnerClass 函数将返回 None。这会导致 create_OuterClass 函数中的 InnerClass_instances 列表包含 None 值,从而导致错误。

可以修改 create_OuterClass 函数以正确处理这种情况,如下所示:

def create_OuterClass(dct):
    if '__OuterClass__' in dct:
        arg = dct['arg']
        InnerClass_instances = [create_InnerClass(dct2) for dct2 in dct['InnerClass_instances']]
        return OuterClass(arg, *InnerClass_instances)
    else:
        return dct

现在,当 create_OuterClass 函数应用于不是 OuterClass 实例的词典时,它将简单地返回该词典。这将防止出现 TypeError 错误,并允许正确解析 JSON 对象。

以下代码提供了完整的示例:

class OuterClass:
    def __init__(self, arg, *InnerClass_instances):
        self.arg = arg
        self.class2 = InnerClass_instances

class InnerClass:
    def __init__(self, name, value):
        self.name = name
        self.value = value

def create_InnerClass(dct):
    if '__InnerClass__' in dct:
        name = dct['name']
        value = dct['value']
        return InnerClass(name, value)

def create_OuterClass(dct):
    if '__OuterClass__' in dct:
        arg = dct['arg']
        InnerClass_instances = [create_InnerClass(dct2) for dct2 in dct['InnerClass_instances']]
        return OuterClass(arg, *InnerClass_instances)
    else:
        return dct

s = """
{
"__OuterClass__": true,
"arg": 4,
"InnerClass_instances":
    [
    {"__InnerClass__": true, "name": "Joe", "value": 12},
    {"__InnerClass__": true, "name": "Jimmy", "value":7}
    ]
}
"""

outerClass = json.loads(s, object_hook=create_OuterClass)

print(outerClass.arg)
# 4
print(outerClass.class2[0].name)
# Joe
print(outerClass.class2[1].name)
# Jimmy