Python:JSON 里有金额时,`parse_float=Decimal` 省心点

52 次浏览3 条回复

有些接口返回金额、汇率这种小数,直接 json.loads() 会进到 float。展示还行,真要做累加就容易有点飘。

环境:Python 3.10+,只用标准库。

import json
from decimal import Decimal

text = '{"price": 0.1, "tax": 0.2}'

plain = json.loads(text)
exact = json.loads(text, parse_float=Decimal)

print(plain["price"] + plain["tax"])
print(exact["price"] + exact["tax"])
print(type(exact["price"]))

float 那行会看到一点点偏差,Decimal 就稳多了。要是后面还要写回 JSON,记得自己处理一下序列化。

顺手补个小坑:如果接口里还有科学计数法,parse_float=Decimal 也一样能接住,比如 1e-6。

另外要是连整数也想保持成 Decimal,可以再加 parse_int=Decimal,不过一般金额场景我也只会先把小数这块稳住。

补一句,Decimal 进 json.dumps() 默认会报类型问题。要是只是先落日志或调试,default=str 能顶一下;但真要给下游用,最好还是自己定一下序列化方式,不然数值可能被你悄悄变成字符串。

隔壁空白Lv1#2

补一句,Decimal 进 json.dumps() 默认会报类型问题。要是只是先落日志或调试,default=str 能顶一下;但真要给下游用,最好还是自己定一下序列化方式,不然数值可能被你悄悄变成字符串。

再补个实际一点的写法:如果只是想保住金额精度,json.loads(..., parse_float=Decimal) 够了;后面要转字符串再输出,最好单独包一层,别直接把 Decimal 扔给 json.dumps。

像这样会更稳一点:

import json
from decimal import Decimal

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

print(json.dumps({"price": Decimal("0.30")}, cls=DecimalEncoder))

如果是给下游系统用,str 这步至少是显式的,不会悄悄丢精度。