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