我有一个十进制('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,它应该看起来像{'x': 3.9}。我不关心客户端的精度,所以浮点数很好。
有什么好方法来序列化它吗?JSONDecoder不接受Decimal对象,提前转换为浮点数会产生{'x': 3.8999999999999999},这是错误的,而且会浪费大量带宽。
我有一个十进制('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,它应该看起来像{'x': 3.9}。我不关心客户端的精度,所以浮点数很好。
有什么好方法来序列化它吗?JSONDecoder不接受Decimal对象,提前转换为浮点数会产生{'x': 3.8999999999999999},这是错误的,而且会浪费大量带宽。
当前回答
您可以根据需要创建一个自定义JSON编码器。
import json
from datetime import datetime, date
from time import time, struct_time, mktime
import decimal
class CustomJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return str(o)
if isinstance(o, date):
return str(o)
if isinstance(o, decimal.Decimal):
return float(o)
if isinstance(o, struct_time):
return datetime.fromtimestamp(mktime(o))
# Any other serializer if needed
return super(CustomJSONEncoder, self).default(o)
解码器可以这样命名,
import json
from decimal import Decimal
json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)
输出将是:
>>'{"x": 3.9}'
其他回答
我将分享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)
如果有人还在寻找答案,那么很有可能您的数据中有一个“NaN”,您正在尝试对其进行编码。因为NaN被Python视为浮点数。
您可以根据需要创建一个自定义JSON编码器。
import json
from datetime import datetime, date
from time import time, struct_time, mktime
import decimal
class CustomJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return str(o)
if isinstance(o, date):
return str(o)
if isinstance(o, decimal.Decimal):
return float(o)
if isinstance(o, struct_time):
return datetime.fromtimestamp(mktime(o))
# Any other serializer if needed
return super(CustomJSONEncoder, self).default(o)
解码器可以这样命名,
import json
from decimal import Decimal
json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)
输出将是:
>>'{"x": 3.9}'
这是我从我们的课程中提取出来的
class CommonJSONEncoder(json.JSONEncoder):
"""
Common JSON Encoder
json.dumps(myString, cls=CommonJSONEncoder)
"""
def default(self, obj):
if isinstance(obj, decimal.Decimal):
return {'type{decimal}': str(obj)}
class CommonJSONDecoder(json.JSONDecoder):
"""
Common JSON Encoder
json.loads(myString, cls=CommonJSONEncoder)
"""
@classmethod
def object_hook(cls, obj):
for key in obj:
if isinstance(key, six.string_types):
if 'type{decimal}' == key:
try:
return decimal.Decimal(obj[key])
except:
pass
def __init__(self, **kwargs):
kwargs['object_hook'] = self.object_hook
super(CommonJSONDecoder, self).__init__(**kwargs)
通过unittest的:
def test_encode_and_decode_decimal(self):
obj = Decimal('1.11')
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)
obj = {'test': Decimal('1.11')}
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)
obj = {'test': {'abc': Decimal('1.11')}}
result = json.dumps(obj, cls=CommonJSONEncoder)
self.assertTrue('type{decimal}' in result)
new_obj = json.loads(result, cls=CommonJSONDecoder)
self.assertEqual(new_obj, obj)
这个问题很老了,但是对于大多数用例,Python3中似乎有一个更好、更简单的解决方案:
number = Decimal(0.55)
converted_number = float(number) # Returns: 0.55 (as type float)
你可以把Decimal转换成float。