【发布时间】:2017-09-03 07:31:56
【问题描述】:
我无法理解 json.loads() 中的 object_hook 功能是如何工作的。我在这里发现了一个类似的问题object_hook does not address the full json,但是我尝试按照我从中理解的内容进行操作,但它仍然对我不起作用。我已经收集到 object_hook 函数以某种方式被递归调用,但我无法理解如何使用它从 json 字符串构造复杂的对象层次结构。考虑以下 json 字符串、类和 object_hook 函数:
import json
from pprint import pprint
jstr = '{"person":{ "name": "John Doe", "age": "46", \
"address": {"street": "4 Yawkey Way", "city": "Boston", "state": "MA"} } }'
class Address:
def __init__(self, street=None, city=None, state=None):
self.street = street
self.city = city
self.state = state
class Person:
def __init__(self, name=None, age=None, address=None):
self.name = name
self.age = int(age)
self.address = Address(**address)
def as_person(jdict):
if u'person' in jdict:
print('person found')
person = jdict[u'person']
return Person(name=person[u'name'], age=person[u'age'],
address=person[u'address'])
else:
return('person not found')
return jdict
(我用关键字 args 定义类来提供默认值,这样 json 就不需要包含所有元素,并且我仍然可以确保属性存在于类实例中。我最终也会将方法与类关联,但是想要从 json 数据填充实例。)
如果我跑步:
>>> p = as_person(json.loads(jstr))
我得到了我的期望,即:
person found
而p变成了Person对象,即:
>>> pprint(p.__dict__)
{'address': <__main__.Address instance at 0x0615F3C8>,
'age': 46,
'name': u'John Doe'}
>>> pprint(p.address.__dict__)
{'city': u'Boston', 'state': u'MA', 'street': u'4 Yawkey Way'}
但是,如果相反,我尝试使用:
>>> p = json.loads(jstr, object_hook=as_person)
我明白了:
person found
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "C:\Program Files (x86)\Python27\lib\json\__init__.py", line 339, in loads
return cls(encoding=encoding, **kw).decode(s)
File "C:\Program Files (x86)\Python27\lib\json\decoder.py", line 366, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Program Files (x86)\Python27\lib\json\decoder.py", line 382, in
raw_decode
obj, end = self.scan_once(s, idx)
File "<interactive input>", line 5, in as_person
TypeError: string indices must be integers, not unicode
我不知道为什么会发生这种情况,并且怀疑 object_hook 机制的工作方式存在一些我遗漏的微妙之处。
为了结合上述问题的概念,即 object_hook 从下到上评估每个嵌套字典(并在遍历中替换它?)我也尝试过:
def as_person2(jdict):
if u'person' in jdict:
print('person found')
person = jdict[u'person']
return Person2(name=person[u'name'], age=person[u'age'], address=person[u'address'])
elif u'address' in jdict:
print('address found')
return Address(jdict[u'address'])
else:
return('person not found')
return jdict
>>> json.loads(jstr, object_hook=as_person2)
address found
person found
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
File "C:\Program Files (x86)\Python27\lib\json\__init__.py", line 339, in loads
return cls(encoding=encoding, **kw).decode(s)
File "C:\Program Files (x86)\Python27\lib\json\decoder.py", line 366, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Program Files (x86)\Python27\lib\json\decoder.py", line 382, in raw_decode
obj, end = self.scan_once(s, idx)
File "<interactive input>", line 5, in as_person2
AttributeError: Address instance has no attribute '__getitem__'
所以,很明显,object_hook 函数的正确形式正在逃避我。
谁能详细解释一下 object_hook 机制是如何工作的,以及结果对象树应该如何自下而上递归构造,为什么我的代码不能按预期工作,或者修复我的示例或提供一个使用 object_hook 函数来构建一个复杂的类,假设你只得到一个 object_hook 函数?
【问题讨论】:
-
你确认
person的内容是什么了吗? -
我意识到我忘了在上面发布我的 Person2 类;我将 Person 类中的地址分配更改为 self.address = address
-
@ScottHunter:如果你的意思是 p,是的;这是一个 Person 实例:<__main__.person instance at>。 (其他电话从来没有那么远。)