我有一个十进制('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,它应该看起来像{'x': 3.9}。我不关心客户端的精度,所以浮点数很好。

有什么好方法来序列化它吗?JSONDecoder不接受Decimal对象,提前转换为浮点数会产生{'x': 3.8999999999999999},这是错误的,而且会浪费大量带宽。


当前回答

我将分享flask 2.1.0对我有用的东西 当我创建字典时,必须从jsonify使用我使用四舍五入:

json_dict['price'] = round(self.price, ndigits=2) if self.price else 0

这样我就可以返回D.DD号或0,而不需要使用全局配置。这很好,因为有些十进制坐标需要更大,比如经纬度坐标。

return jsonify(json_dict)

其他回答

基于stdOrgnlDave的答案,我已经定义了这个包装器,它可以被可选的类型调用,所以编码器将只适用于项目中的某些类型。我相信工作应该在你的代码中完成,而不是使用这个“默认”编码器,因为“显式比隐式更好”,但我知道使用这将节省你的一些时间。: -)

import time
import json
import decimal
from uuid import UUID
from datetime import datetime

def JSONEncoder_newdefault(kind=['uuid', 'datetime', 'time', 'decimal']):
    '''
    JSON Encoder newdfeault is a wrapper capable of encoding several kinds
    Use it anywhere on your code to make the full system to work with this defaults:
        JSONEncoder_newdefault()  # for everything
        JSONEncoder_newdefault(['decimal'])  # only for Decimal
    '''
    JSONEncoder_olddefault = json.JSONEncoder.default

    def JSONEncoder_wrapped(self, o):
        '''
        json.JSONEncoder.default = JSONEncoder_newdefault
        '''
        if ('uuid' in kind) and isinstance(o, uuid.UUID):
            return str(o)
        if ('datetime' in kind) and isinstance(o, datetime):
            return str(o)
        if ('time' in kind) and isinstance(o, time.struct_time):
            return datetime.fromtimestamp(time.mktime(o))
        if ('decimal' in kind) and isinstance(o, decimal.Decimal):
            return str(o)
        return JSONEncoder_olddefault(self, o)
    json.JSONEncoder.default = JSONEncoder_wrapped

# Example
if __name__ == '__main__':
    JSONEncoder_newdefault()

3.9不能准确地在IEEE浮点数中表示,它总是以3.899999999999999999表示,例如,尝试打印repr(3.9),你可以在这里阅读更多信息:

http://en.wikipedia.org/wiki/Floating_point http://docs.sun.com/source/806-3568/ncg_goldberg.html

所以如果你不想要float,唯一的选项你必须将它作为字符串发送,并允许自动将十进制对象转换为JSON,做这样的事情:

import decimal
from django.utils import simplejson

def json_encode_decimal(obj):
    if isinstance(obj, decimal.Decimal):
        return str(obj)
    raise TypeError(repr(obj) + " is not JSON serializable")

d = decimal.Decimal('3.5')
print simplejson.dumps([d], default=json_encode_decimal)

如果你想传递一个包含小数的字典到请求库(使用json关键字参数),你只需要安装simplejson:

$ pip3 install simplejson    
$ python3
>>> import requests
>>> from decimal import Decimal
>>> # This won't error out:
>>> requests.post('https://www.google.com', json={'foo': Decimal('1.23')})

这个问题的原因是,请求只在simplejson存在时才使用它,如果它没有安装,则回落到内置json。

这个问题很老了,但是对于大多数用例,Python3中似乎有一个更好、更简单的解决方案:

number = Decimal(0.55)
converted_number = float(number) # Returns: 0.55 (as type float)

你可以把Decimal转换成float。

我想让每个人都知道,我在运行Python 2.6.5的网络服务器上尝试了michaowarmarczyk的答案,它工作得很好。然而,我升级到Python 2.7,它停止工作了。我试着想一些方法来编码十进制对象,这就是我想到的:

import decimal

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

注意,这将把小数转换为它的字符串表示形式(例如;“1.2300”),以a.不丢失有效数字和b.防止舍入错误。

希望这能帮助任何在使用Python 2.7时遇到问题的人。我测试过了,它似乎工作得很好。如果有人在我的解决方案中注意到任何错误或提出了更好的方法,请告诉我。

使用的例子:

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