Python JSON序列化一个Decimal对象
我有一个Decimal('3.9')
作为对象的一部分,并希望将其编码为一个JSONstring,它应该看起来像{'x': 3.9}
。 我不关心客户端的精确度,所以浮点数是好的。
有没有序列化这个好方法? JSONDecoder不接受Decimal对象,并事先转换为浮点数,产生{'x': 3.8999999999999999}
这是错误的,将是一个很大的带宽浪费。
如何子类json.JSONEncoder
?
class DecimalEncoder(json.JSONEncoder): def _iterencode(self, o, markers=None): 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)._iterencode(o, markers)
然后像这样使用它:
json.dumps({'x': decimal.Decimal('5.5')}, cls=DecimalEncoder)
Simplejson 2.1及更高版本具有对十进制types的本机支持:
>>> 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):
所以:
>>> json.dumps(Decimal('3.9')) '3.9'
希望这个function将被包含在标准库中。
我想让每个人都知道我在运行Python 2.6.5的Web服务器上尝试了MichałMarczyk的答案,并且它工作正常。 但是,我升级到Python 2.7并停止工作。 我试图想出一些方法来编码小数对象,这就是我想到的:
class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): return float(o) return super(DecimalEncoder, self).default(o)
这应该有希望帮助任何有问题的人使用Python 2.7。 我testing了它,似乎工作正常。 如果有人注意到我的解决scheme中的任何错误或想出更好的方法,请让我知道。
我尝试从simplejson切换到GAE 2.7的内置json,并且遇到了十进制问题。 如果默认返回的str(o)有引号(因为_iterencode在默认结果上调用_iterencode),而float(o)会删除尾部0。
如果默认返回一个inheritance自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]'
3.9
不能完全代表IEEE浮点数,它总是会是3.8999999999999999
,例如试试print repr(3.9)
,你可以在这里阅读更多关于它:
http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html
所以,如果你不想浮动,只有选项,你必须发送它作为string,并允许小数对象自动转换为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)
在我的烧瓶应用程序,它使用python 2.7.11,烧瓶炼金术(与'db.decimal'types),烧瓶棉花糖('即时'串行器和解串器),我有这个错误,每次我做了一个GET或POST 。 序列化器和反序列化器未能将Decimaltypes转换为任何JSON可识别的格式。
我做了一个“点子安装simplejson”,然后通过添加
import simplejson as json
串行器和解串器开始再次发声。 我什么也没有做… DEciamls显示为'234.00'浮点格式。
我的.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
让我的生活变得如此简单
这是我的,从我们的课堂中提取的
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)
这可以通过添加完成
elif isinstance(o, decimal.Decimal): yield str(o)
在\Lib\json\encoder.py:JSONEncoder._iterencode
,但我希望有一个更好的解决scheme