将Python对象列表序列化为JSON

6

我正在尝试将一个对象的__dict__序列化为JSON,这一步进行得很顺利,但是当我向第一个对象的一个实例属性附加对象时,就出现了问题:

from json import dumps

class A(object):
    def __init__(self):
        self.b_list = []

class B(object):
    def __init__(self):
        self.x = 'X'
        self.y = 'Y'
    def __repr__(self):
        return dumps(self.__dict__)

a = A()

print dumps(a.__dict__)  # works fine

a.b_list.append(B())

print dumps(a.__dict__)

第二次调用dumps时,我遇到了以下TypeError错误:
TypeError: {"y": "Y", "x": "X"} is not JSON serializable

我不明白为什么我一直收到这个错误,而我看不出为什么它不能序列化为JSON。

1个回答

7

那是因为B的实例不是一个简单类型。因为您给了B一个__repr__方法,所以该实例会被打印为它的JSON表示,但它本身不是支持的JSON类型。

移除__repr__方法,回溯信息就不会那么混乱:

>>> class A(object):
...     def __init__(self):
...         self.b_list = []
... 
>>> class B(object):
...     def __init__(self):
...         self.x = 'X'
...         self.y = 'Y'
... 
>>> a = A()
>>> a.b_list.append(B())
>>> 
>>> print dumps(a.__dict__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/json/__init__.py", line 243, in dumps
    return _default_encoder.encode(obj)
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/json/encoder.py", line 207, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/json/encoder.py", line 270, in iterencode
    return _iterencode(o, 0)
  File "/Users/mj/Development/Library/buildout.python/parts/opt/lib/python2.7/json/encoder.py", line 184, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <__main__.B object at 0x10a753e10> is not JSON serializable

使用default关键字参数来编码自定义对象:

def encode_b(obj):
    if isinstance(obj, B):
        return obj.__dict__
    return obj

json.dumps(a, default=encode_b)

示例:

>>> def encode_b(obj):
...     if isinstance(obj, B):
...         return obj.__dict__
...     return obj
... 
>>> dumps(a.__dict__, default=encode_b)
'{"b_list": [{"y": "Y", "x": "X"}]}'

感谢您的解释和演示!我以为json.dumps会使用__repr__来序列化对象,但是没有__repr__确实更清晰明了。 - evuez
这对我来说没有意义:如果B的repr返回一个字符串,那么这不是一个简单类型吗?这并没有澄清对JSON编码repr的理解 :( - Tjorriemorrie
1
@Tjorriemorrie:JSON 不使用 __repr__。 OP 尝试通过给 B 的实例提供一个 __repr__ 方法使它们可以被序列化为 JSON,但是这种方法在这里根本不起作用。 - Martijn Pieters
有没有一种方法可以向对象添加一个装饰器,以便在编码时自动处理encode逻辑/过程,而无需扩展JsonEncoder?(我假设json在对象上没有寻找这样的属性?) - Tjorriemorrie
@Tjorriemorrie:你可以使用装饰器,但你仍然需要扩展JSONEncoder,因为确实没有编码器要查找的属性。Pyramid项目扩展了他们的编码器以查找__json__方法,但同样需要自定义编码器。 - Martijn Pieters
好的,谢谢,我现在明白了。那我就使用这个作为我的解决方案吧。 - Tjorriemorrie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接