Python JSON 序列化 Decimal 对象

354 投票
24 回答
388388 浏览
提问于 2025-04-15 17:21

我有一个对象里面有个 Decimal('3.9'),我想把它转成一个JSON字符串,格式应该是 {'x': 3.9}。我不在乎客户端的精度,所以用浮点数就可以了。

有没有好的方法来处理这个?JSONDecoder不支持Decimal对象,提前转换成浮点数的话会变成 {'x': 3.8999999999999999},这就不对了,而且会浪费很多带宽。

24 个回答

270

我想告诉大家,我在运行Python 2.6.5的网络服务器上试过Michał Marczyk的答案,效果很好。不过,当我升级到Python 2.7后,它就不再工作了。我试着想办法对Decimal对象进行编码,最后想出了这个方法:

import decimal

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return str(o)
        return super().default(o)

请注意,这个方法会把小数转换成字符串形式(比如:"1.2300"),这样可以避免丢失重要的数字,并且防止出现四舍五入的错误。

希望这能帮助到那些在使用Python 2.7时遇到问题的人。我测试过,效果不错。如果有人发现我这个方法的bug,或者有更好的解决办法,请告诉我。

使用示例:

json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)
277

Simplejson 2.1及更高版本原生支持Decimal类型:

>>> import simplejson as json

>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'

请注意,use_decimal默认值为True

def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
    allow_nan=True, cls=None, indent=None, separators=None,
    encoding='utf-8', default=None, use_decimal=True,
    namedtuple_as_object=True, tuple_as_array=True,
    bigint_as_string=False, sort_keys=False, item_sort_key=None,
    for_json=False, ignore_nan=False, **kw):

所以:

>>> json.dumps(Decimal('3.9'))
'3.9'

希望这个功能能被加入到标准库中。

182

那我们来看看怎么去扩展一下 json.JSONEncoder 这个类吧。

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            # wanted a simple yield str(o) in the next line,
            # but that would mean a yield on the line with super(...),
            # which wouldn't work (see my comment below), so...
            return (str(o) for o in [o])
        return super(DecimalEncoder, self).default(o)

然后可以这样使用它:

json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)

撰写回答