Python 3.5如何将Decimal对象序列化为JSON

5

我需要将十进制值999999.99990000005编码为JSON格式,同时不失去精度,也不改变其表示方式为字符串。期望得到的结果是{ "prc" : 999999.99990000005 }

从[这篇帖子][1]中,我找到了以下代码。

import json
import decimal

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

y = { 'prc' : decimal.Decimal('999999.99990000005')}

但它会生成一个字符串。
json.dumps(y, cls=DecimalEncoder)

'{"cPrc": "999999.99990000005"}'

isinstance中用float(o)替换str(o)会截断数字。 有没有办法得到非字符串结果? 附注:我不能使用任何外部模块,如simplejson。
编辑: 如果我将值保留为字符串,则以下内容也会生成字符串。
>>> x = json.loads("""{ "cPrc" : "999999.99990000005" }""", parse_float=decimal.Decimal)
>>> x
{'cPrc': '999999.99990000005'}

1
你确定要这样做吗?当读取JSON时,你几乎肯定会失去那种精度,因为大多数库都会将其作为浮点数导入。 - Linuxios
字符串结果有什么问题?为什么不能使用它? - Artem Kolontay
请参见问题中的编辑,了解为什么我无法保留字符串。 - Steve
@Steve 你说你不能使用像simplejson这样的外部模块,所以我假设你知道simplejson可以免费编码/解码十进制值,对吧? - BPL
它序列化为字符串,因为您所需的精度级别比JSON支持的基本数据类型能够处理的任何精度都要高。您可能想要做的是将其序列化为类似当前操作的字符串,然后调整反序列化过程以将其处理为十进制数。 - Hamms
有只漫游的驼鹿 - NeilG
3个回答

4

虽然不是最漂亮的方式,但如果你坚持使用json,我们可以创建一个自定义解码器,并让我们的编码器在处理十进制数据时指定类型。

class DecimalEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, decimal.Decimal):
            return {
                "_type": "decimal",
                "value": str(obj)
            }
        return super(DecimalEncoder, self).default(obj)

上面的代码将十进制类型作为我们解码器的标识,同时编码十进制为字符串以保持精度。

class DecimalDecoder(json.JSONDecoder):
    def __init__(self, *args, **kwargs):
        json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)

    def object_hook(self, obj):
        if '_type' not in obj:
            return obj
        type = obj['_type']
        if type == 'decimal':
            return decimal.Decimal(obj['value'])
        return obj

解码器检查我们的十进制类型标志,如果是,则使用十进制构造函数。对于所有其他情况,它使用默认解码。
input = { 'prc' : decimal.Decimal('999999.99990000005')}
encoded = json.dumps(input, cls=DecimalEncoder)
decoded = json.loads(encoded, cls=DecimalDecoder)

最终结果应该将我们的输入进行编码,然后将结果解码为十进制对象。

3

回答自己的问题。 我会输出用特殊字符 `. 包围的十进制数对象。然后从文本中删除它和双引号。

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return '`'+str(o)+'`' #` is special, will be removed later
        return super(DecimalEncoder, self).default(o)

json.dumps(y, cls=DecimalEncoder).replace("\"`",'').replace("`\"",'')

-1

从版本 3.1 开始,json 库支持 parse_float 选项,允许编码/解码为 Decimal。

请参考 官方文档 获取使用示例。


此标志仅支持解码操作。 - HYBRID BEING

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