在使用 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