Python JSON序列化十进制对象
我有一个Python JSON序列化十进制对象,python,json,floating-point,decimal,Python,Json,Floating Point,Decimal,我有一个Decimal('3.9')作为对象的一部分,并希望将其编码为一个JSON字符串,该字符串应类似于{'x':3.9}。我不关心客户端的精确性,所以浮动是可以的 有没有一个好的方法来序列化这个?JSONDecoder不接受十进制对象,预先转换为浮点会产生{'x':3.899999999999999999},这是错误的,并且会极大地浪费带宽。3.9不能在IEEE浮点中准确表示,它总是以3.8999999999999的形式出现,例如,尝试打印repr(3.9),您可以在此处阅读更多信息:
Decimal('3.9')
作为对象的一部分,并希望将其编码为一个JSON字符串,该字符串应类似于{'x':3.9}
。我不关心客户端的精确性,所以浮动是可以的
有没有一个好的方法来序列化这个?JSONDecoder不接受十进制对象,预先转换为浮点会产生
{'x':3.899999999999999999}
,这是错误的,并且会极大地浪费带宽。3.9
不能在IEEE浮点中准确表示,它总是以3.8999999999999
的形式出现,例如,尝试打印repr(3.9)
,您可以在此处阅读更多信息:
所以,如果您不想使用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)
3.9
无法在IEEE浮点数中精确表示,它将始终以3.899999999999999
的形式出现,例如,尝试print repr(3.9)
,您可以在此处阅读更多信息:
所以,如果您不想使用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)
这可以通过添加
elif isinstance(o, decimal.Decimal):
yield str(o)
在
\Lib\json\encoder.py:jsonecoder.\u iterencode
中,我希望有一个更好的解决方案,可以通过添加
elif isinstance(o, decimal.Decimal):
yield str(o)
在
\Lib\json\encoder.py:JSONEncoder.\u iterencode
中,但我希望有更好的解决方案子类化json.JSONEncoder
怎么样
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
# wanted a simple yield str(o) in the next line,
# but that would mean a yield on the line with super(...),
# which wouldn't work (see my comment below), so...
return (str(o) for o in [o])
return super(DecimalEncoder, self).default(o)
然后像这样使用它:
json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)
子类化
json.jsonecoder
怎么样
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
# wanted a simple yield str(o) in the next line,
# but that would mean a yield on the line with super(...),
# which wouldn't work (see my comment below), so...
return (str(o) for o in [o])
return super(DecimalEncoder, self).default(o)
然后像这样使用它:
json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)
和更高版本本机支持十进制类型:
>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'
请注意,默认情况下,use\u 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):
因此:
希望此功能将包含在标准库中。及更高版本本机支持十进制类型:
>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'
请注意,默认情况下,use\u 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):
因此:
希望此功能将包含在标准库中。我想让大家知道,我在运行Python 2.6.5的web服务器上尝试了MichałMarczyk的答案,效果很好。然而,我升级到了Python2.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中遇到问题的人。我测试过它,它似乎工作得很好。如果有人注意到我的解决方案中有任何错误或想出更好的方法,请告诉我。我想让大家知道,我在运行Python 2.6.5的web服务器上尝试了MichałMarczyk的答案,效果很好。然而,我升级到了Python2.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中遇到问题的人。我测试过它,它似乎工作得很好。如果有人注意到我的解决方案中存在任何错误或提出了更好的方法,请告诉我。我尝试过从simplejson切换到GAE 2.7的内置json,但十进制有问题。如果default返回str(o),则有引号(因为_iterencode对default的结果调用_iterencode),而float(o)将删除尾随的0 如果default返回一个类的对象,该类继承自float(或任何调用repr而无需附加格式)并具有自定义_repr________________________________
import json
from decimal import Decimal
class fakefloat(float):
def __init__(self, value):
self._value = value
def __repr__(self):
return str(self._value)
def defaultencode(o):
if isinstance(o, Decimal):
# Subclass float with custom repr?
return fakefloat(o)
raise TypeError(repr(o) + " is not JSON serializable")
json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
'[10.2, "10.20", 10.20]'
我尝试过在GAE2.7中从simplejson切换到内置json,但遇到了十进制的问题。如果default返回str(o),则有引号(因为_iterencode对default的结果调用_iterencode),而float(o)将删除尾随的0 如果default返回一个类的对象,该类继承自float(或任何调用repr而无需附加格式)并具有自定义_repr________________________________
import json
from decimal import Decimal
class fakefloat(float):
def __init__(self, value):
self._value = value
def __repr__(self):
return str(self._value)
def defaultencode(o):
if isinstance(o, Decimal):
# Subclass float with custom repr?
return fakefloat(o)
raise TypeError(repr(o) + " is not JSON serializable")
json.dumps([10.20, "10.20", Decimal('10.20')], default=defaultencode)
'[10.2, "10.20", 10.20]'
这是我从我们班上学到的
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)
通过单元测试的:
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)
这是我从我们班上学到的
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)
通过单元测试的:
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)
在我的Flask应用程序中,它使用python 2.7.11、Flask alchemy(使用'db.decimal'类型)和Flask Marshmallow(用于'instant'序列化程序和反序列化程序),每次执行GET或POST时,我都会遇到这个错误。序列化程序和反序列化程序未能将十进制类型转换为任何可识别的JSON格式 然后我做了一个“pip安装simplejson” 只要加上
import simplejson as json
序列化程序和反序列化程序再次开始发出呜呜声。我什么也没做。。。
十进制以“234.00”浮点格式显示。在我的Flask应用程序中,它使用python 2.7.11、Flask alchemy(使用“db.decimal”类型)和Flask Marshmallow(用于“即时”序列化程序和反序列化程序),每次执行GET或POST时都会出现此错误。序列化程序和反序列化程序未能将十进制类型转换为任何可识别的JSON格式 然后我做了一个“pip安装simplejson” 只要加上
import simplejson as json
序列化程序和反序列化程序再次开始发出呜呜声。我什么也没做。。。
十进制以“234.00”浮点格式显示。My$.02
我扩展了一系列JSON编码器,因为我正在为我的web服务器序列化成吨的数据。这里有一些很好的代码。请注意,它很容易扩展到您喜欢的几乎任何数据格式,并将3.9复制为“thing”:3.9
JSONEncoder_olddefault = json.JSONEncoder.default
def JSONEncoder_newdefault(self, o):
if isinstance(o, UUID): return str(o)
if isinstance(o, datetime): return str(o)
if isinstance(o, time.struct_time): return datetime.fromtimestamp(time.mktime(o))
if isinstance(o, decimal.Decimal): return str(o)
return JSONEncoder_olddefault(self, o)
json.JSONEncoder.default = JSONEncoder_newdefault
让我的生活变得更加轻松…我的$0.02
我扩展了一系列JSON编码器,因为我正在为我的web服务器序列化成吨的数据。
import decimal
data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)
>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 1e-07}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01733}'
import json
from collections.abc import Mapping, Iterable
from decimal import Decimal
class DecimalEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, Mapping):
return '{' + ', '.join(f'{self.encode(k)}: {self.encode(v)}' for (k, v) in obj.items()) + '}'
if isinstance(obj, Iterable) and (not isinstance(obj, str)):
return '[' + ', '.join(map(self.encode, obj)) + ']'
if isinstance(obj, Decimal):
return f'{obj.normalize():f}' # using normalize() gets rid of trailing 0s, using ':f' prevents scientific notation
return super().encode(obj)
>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 0.0000001}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01734}'
total_development_cost_var = process_assumption_objects.values('total_development_cost').aggregate(sum_dev = Sum('total_development_cost', output_field=FloatField()))
total_development_cost_var = list(total_development_cost_var.values())