为什么JSONEncoder无法用于namedtuple? [英] Why doesn't JSONEncoder work for namedtuples?
问题描述
我无法转储collections.namedtuple
作为正确的JSON.
I am unable to to dump collections.namedtuple
as correct JSON.
第一 ,请考虑使用自定义JSON序列化程序的官方示例:
import json
class ComplexEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, complex):
return [obj.real, obj.imag]
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, obj)
json.dumps(2 + 1j, cls=ComplexEncoder) # works great, without a doubt
第二 ,现在考虑以下示例,该示例告诉Python如何对Friend
对象进行JSON化:
Second, now consider the following example which tells Python how to JSONize a Friend
object:
import json
class Friend():
""" struct-like, for storing state details of a friend """
def __init__(self, _id, f_name, l_name):
self._id = _id
self.f_name = f_name
self.l_name = l_name
t = Friend(21, 'Steve', 'Rogerson')
class FriendEncoder(json.JSONEncoder):
""" take a Friend object and make it truly json """
def default(self, aFriend):
if isinstance(aFriend, Friend):
return {
"id": aFriend._id,
"f_name": aFriend.f_name,
"l_name": aFriend.l_name,
}
return super(FriendEncoder, self).default(aFriend)
json.dumps(t, cls=FriendEncoder) # returns correctly JSONized string
最后 ,当我们尝试使用namedtuples实现同一事物时,json.dumps(t, cls=FriendEncoder)
不会给出任何错误,但是给出错误的输出.看看:
Finally when we try to implement the same thing using namedtuples, json.dumps(t, cls=FriendEncoder)
doesn't give any errors but gives the wrong output. Take a look:
import pdb
import json
from collections import namedtuple
Friend = namedtuple("Friend", ["id", 'f_name', 'l_name'])
t = Friend(21, 'Steve', 'Rogerson')
print(t)
class FriendEncoder(json.JSONEncoder):
""" take a Friend collections.namedtuple object and make it truly json """
def default(self, obj):
if True: # if isinstance(obj, Friend):
ans = dict(obj._asdict())
pdb.set_trace() # WOW!! even after commenting out the if and hardcoding True, debugger doesn't get called
return ans
return json.JSONEncoder.default(self, obj)
json.dumps(t, cls=FriendEncoder)
我得到的输出不是类似dict的,而是一个值列表,即[21, 'Steve', 'Rogerson']
The output I get is not a dict-like but rather just a list of values i.e. [21, 'Steve', 'Rogerson']
默认行为是信息丢失吗?
json.dumps是否忽略显式传递的编码器?
Is the default behavior such that information is lost?
Does json.dumps ignore the explicitly passed encoder?
通过正确的json化namedtuple,我的意思是json.dumps应该返回 exactly dict(nt._asdict())
之类的数据,其中nt
是预定义的namedtuple
by correctly jsonized namedtuple I mean that json.dumps should return data like exactly dict(nt._asdict())
, where nt
is a pre defined namedtuple
推荐答案
正如我在评论中所说,json.JSONEncoder
仅在遇到对象类型时才调用default
,它不知道如何序列化自身.在表中, c8>文档.这是它的屏幕截图,以方便参考:
As I said in a comment, the json.JSONEncoder
only calls default
when it encounters an object type it doesn't already know how to serialize itself. There's a table of them in the json
documentation. Here's a screenshot of it for easy reference:
请注意,tuple
在列表中,并且由于namedtuple
是tuple
的子类,因此它也适用于它们. (即因为isinstance(friend_instance, tuple)
→True
).
Note that tuple
is on the list, and since namedtuple
is a subclasses of tuple
, so it applies to them, too. (i.e. because isinstance(friend_instance, tuple)
→ True
).
这就是为什么从未调用用于处理Friend
类实例的代码的原因.
This is why your code for handling instances of the Friend
class never gets called.
以下是一种解决方法-即通过创建一个简单的Wrapper
类,该类的实例不会是json.JSONEncoder
认为已经知道如何处理的类型,然后指定一个default=
关键字自变量函数,遇到某个对象尚不知道如何操作时将调用该函数.
Below is one workaround — namely by creating a simple Wrapper
class whose instances won't be a type that the json.JSONEncoder
thinks it already knows how to handle, and then specifying a default=
keyword argument function that to be called whenever an object is encountered that it doesn't already know how to do.
这是我的意思:
import json
from collections import namedtuple
class Wrapper(object):
""" Container class for objects with an _asdict() method. """
def __init__(self, obj):
assert hasattr(obj, '_asdict'), 'Cannot wrap object with no _asdict method'
self.obj = obj
if __name__ == '__main__':
Friend = namedtuple("Friend", ["id", 'f_name', 'l_name'])
t = Friend(21, 'Steve', 'Rogerson')
print(t)
print(json.dumps(t))
print(json.dumps(Wrapper(t), default=lambda wrapper: wrapper.obj._asdict()))
输出:
Friend(id=21, f_name='Steve', l_name='Rogerson')
[21, "Steve", "Rogerson"]
{"id": 21, "f_name": "Steve", "l_name": "Rogerson"}
有关其他信息和见解,请查看我对有关问题
For some additional information and insights, also check out my answer to the related question Making object JSON serializable with regular encoder.
这篇关于为什么JSONEncoder无法用于namedtuple?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!