Python的JSON序列化一个小数对象

问题描述 投票:184回答:14

我有一个Decimal('3.9')作为对象的一部分,并希望此编码为JSON字符串,它应该像{'x': 3.9}。我不关心精度在客户端,所以浮动的罚款。

有没有这种序列化的好办法? JSONDecoder不接受小数对象,并转换为浮动事先得到{'x': 3.8999999999999999}这是错误的,将是带宽的巨大浪费。

python json floating-point decimal
14个回答
130
投票

如何继承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)

6
投票

这是我从我们班提取

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)

6
投票

从qazxsw POI,如LinkedIn qazxsw POI:

JSON是不可知的关于数字的语义。在任何编程语言,可以有各种数类型的各种能力和互补,固定或浮动,二进制或十进制的。这可以使不同的编程语言之间的交流困难。 JSON,而不是提供人类使用数只表示:数字序列。所有的编程语言知道如何使数字序列感,即使他们不同意内部表示。这是足以让交汇处。

所以它实际上准确JSON代表的小数位数字(而不是字符串)。娄躺着一个可能的解决问题的方法。

定义自定义JSON编码器:

JSON Standard Document

然后序列化数据时使用它:

json.org

由于从其他的答案评论中指出,将旧版本的Python的威力弄乱表示何时浮动,但不是这种情况了。

要获得小数回在Python:

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)

若要从浮动小数,先将其转换为字符串。


2
投票

基于Decimal(str(value)) 答案我已经定义这个包装,它还可选配多种被称为所以编码器只会工作,某些种类的项目内。我认为工作应该在代码内,并没有这样做,因为“它是优于隐显”使用这种“默认”编码器,但我知道用这会节省一些时间。 :-)

Python 3.0 documentation on decimals

1
投票

您可以创建自定义JSON编码器按您的要求。

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()

和输出将是:

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)

0
投票

如果你想通过含小数到import json from decimal import Decimal json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder) 库(使用>>'{"x": 3.9}' 关键字参数)的字典,你只需要安装requests

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')}) 只有当它是存在的,如果没有安装回落到内置requests


-6
投票

这可以通过添加完成

simplejson

json,但我希望有一个更好的解决方案


199
投票

Simplejson 2.1和较高的有小数类型的本地支持:

>>> json.dumps(Decimal('3.9'), use_decimal=True)
'3.9'

需要注意的是use_decimalTrue默认为:

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'

我们希望,这一功能将被纳入标准库。


157
投票

我希望让大家知道,我试图米哈尔Marczyk的回答是正在运行的Python 2.6.5我的Web服务器上,它工作得很好。不过,我升级到Python 2.7和它停止工作。我试图想某种方式编码的十进制的对象,这是我想出了:

import decimal

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问题的人。我测试了它,它似乎很好地工作。如果有人注意到任何错误在我的解决方案或有更好的办法来了,请让我知道。


30
投票

在我的瓶的应用程序,它用Python 2.7.11,烧瓶炼金术(与“db.decimal”类型),和瓶棉花糖(为“即时”串行器和解串),我有这个错误,每次我做一个GET或POST 。该串行器和解串,未能十进制类型转换成任何JSON格式的识别。

我做了“PIP安装simplejson”,然后就加入

import simplejson as json

在串行器和解串再次开始发出呼噜声。我什么也不做...... DEciamls显示为“234.00”浮点格式。


27
投票

我试图从simplejson切换到内置的JSON GAE 2.7,并与小数的问题。如果默认返回STR(O)有引号(因为_iterencode调用默认的结果_iterencode)和浮点(O)将删除尾随0。

如果默认返回由浮球(或任何没有额外的格式要求再版)继承的类的对象,并有一个自定义__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]'

18
投票

本机的选项丢失,所以我会添加它的下一个家伙/胆囊癌,看起来它。

开始在Django的1.7.x有一个内置的DjangoJSONEncoder,你可以从django.core.serializers.json得到它。

import json
from django.core.serializers.json import DjangoJSONEncoder
from django.forms.models import model_to_dict

model_instance = YourModel.object.first()
model_dict = model_to_dict(model_instance)

json.dumps(model_dict, cls=DjangoJSONEncoder)

普雷斯托!


11
投票

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

所以,如果你不想浮动,唯一的选择,你必须把它发送字符串,并允许小数对象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)

11
投票

我的$ .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

让我的生活变得更轻松?


8
投票

对于Django的用户:

最近碰到TypeError: Decimal('2337.00') is not JSON serializable同时JSON编码即json.dumps(data)

解:

# converts Decimal, Datetime, UUIDs to str for Encoding
from django.core.serializers.json import DjangoJSONEncoder  

json.dumps(response.data, cls=DjangoJSONEncoder)

但是,现在的十进制值是一个字符串,现在对数据进行解码时,使用parse_float选项json.loads我们可以明确地设置小数点/浮点值解析器:

import decimal 

data = json.loads(data, parse_float=decimal.Decimal) # default is float(num_str)
© www.soinside.com 2019 - 2024. All rights reserved.