我有一个十进制('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,它应该看起来像{'x': 3.9}。我不关心客户端的精度,所以浮点数很好。
有什么好方法来序列化它吗?JSONDecoder不接受Decimal对象,提前转换为浮点数会产生{'x': 3.8999999999999999},这是错误的,而且会浪费大量带宽。
我有一个十进制('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,它应该看起来像{'x': 3.9}。我不关心客户端的精度,所以浮点数很好。
有什么好方法来序列化它吗?JSONDecoder不接受Decimal对象,提前转换为浮点数会产生{'x': 3.8999999999999999},这是错误的,而且会浪费大量带宽。
当前回答
我的2美分简单的解决方案,如果你确定Decimal是你的json dumps方法中唯一的坏人:
print(json.loads(json.dumps({
'a': Decimal(1230),
'b': Decimal(11111111123.22),
}, default=lambda x: eval(str(x)))))
>>> {'a': 1230, 'b': 11111111123.22}
这里“聪明”的事情是使用default将Decimal自动转换为int或float,利用eval函数:
但是在你的代码上使用eval时一定要小心,因为这可能会导致安全问题;)
其他回答
我想让每个人都知道,我在运行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)
Simplejson 2.1及更高版本原生支持十进制类型:
>>> 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):
So:
>>> json.dumps(Decimal('3.9'))
'3.9'
希望该特性将包含在标准库中。
如果你想传递一个包含小数的字典到请求库(使用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。
来自JSON标准文档,链接在json.org:
JSON is agnostic about the semantics of numbers. In any programming language, there can be a variety of number types of various capacities and complements, fixed or floating, binary or decimal. That can make interchange between different programming languages difficult. JSON instead offers only the representation of numbers that humans use: a sequence of digits. All programming languages know how to make sense of digit sequences even if they disagree on internal representations. That is enough to allow interchange.
因此,在JSON中将小数表示为数字(而不是字符串)实际上是准确的。这个问题有一个可能的解决办法。
定义一个自定义JSON编码器:
import json
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return super(CustomJsonEncoder, self).default(obj)
然后在序列化数据时使用它:
json.dumps(data, cls=CustomJsonEncoder)
正如其他答案的评论所指出的,旧版本的python在转换为float时可能会弄乱表示,但现在情况已经不同了。
在Python中返回小数:
Decimal(str(value))
这个解决方案在Python 3.0关于小数的文档中有提示:
要从浮点数创建Decimal,首先要将其转换为字符串。
对于Django用户:
最近遇到TypeError: Decimal('2337.00')不是JSON可序列化的 当JSON编码时,即JSON .dump (data)
解决方案:
# converts Decimal, Datetime, UUIDs to str for Encoding
from django.core.serializers.json import DjangoJSONEncoder
json.dumps(response.data, cls=DjangoJSONEncoder)
但是,现在Decimal值将是一个字符串,现在我们可以在解码数据时显式设置Decimal /float值解析器,使用json.loads中的parse_float选项:
import decimal
data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)