为什么我的自定义JSONEncoder.default()忽略了布尔值?

Hip*_*Man 5 python json encoder python-3.x

我想将字典转换为JSON字符串,其中布尔True值转换为数字1,布尔False值转换为数字0.我正在使用一个JSONEncoder子类,但它似乎忽略了布尔...

import json

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, bool):
            return 1 if obj else 0
    return super().default(obj)

data = { 'key-a' : 'a', 'key-true' : True, 'key-false' : False }

jsondata = json.dumps(data, cls=MyEncoder)

print(jsondata)
Run Code Online (Sandbox Code Playgroud)

我希望这是结果:

{"key-true": 1, "key-a": "a", "key-false": 0}
Run Code Online (Sandbox Code Playgroud)

但是,这就是我得到的:

{"key-true": true, "key-a": "a", "key-false": false}
Run Code Online (Sandbox Code Playgroud)

我知道我可以在传递之前以编程方式修改数据json.dumps,但是有什么办法可以通过JSONEncoder子类获得我想要的结果吗?

Zer*_*eus 8

只有当编码器遇到一个它不知道如何序列化的对象时,才会调用子类的default()方法JSONEncoder.

不幸的是,官方文档并没有说清楚.它被提到,但是在类构造函数的"关键字参数"部分中,而不是在方法的文档中:

如果指定,则default应该是为无法以其他方式序列化的对象调用的函数.它应返回对象的JSON可编码版本或引发TypeError.如果未指定,TypeError则引发.

可以轻松验证此行为:

class MyEncoder(json.JSONEncoder):

    def default(self, obj):
        if isinstance(obj, bool):
            print('got bool')
            return 1 if obj else 0
        if isinstance(obj, Foo):
            print('got Foo')
            return {'__Foo__': id(obj)}
        print('got unknown')
        return super().default(obj)
Run Code Online (Sandbox Code Playgroud)

>>> class Foo: pass
...
>>> s = json.dumps({'a': False, 'b': True, 'c': Foo()}, cls=MyEncoder)
got Foo
>>> s
'{"a": false, "c": {"__Foo__": 140636444256856}, "b": true}'
Run Code Online (Sandbox Code Playgroud)

JSONEncoder不是设计为轻松允许覆盖它已知道如何序列化的对象的序列化(这是一件好事:像JSON这样的标准的全部意义在于它们是,嗯,标准)...所以如果你真的想要编码布尔,就好像它们是整数一样,最简单的方法可能是按照问题中的建议预处理数据.