[python] Python JSON은 Decimal 객체를 직렬화합니다.

나는 Decimal('3.9')객체의 일부를 가지고 있으며 이것을 JSON 문자열로 인코딩하고 싶습니다.{'x': 3.9} . 나는 클라이언트 측의 정밀도에 관심이 없으므로 플로트가 좋습니다.

이것을 직렬화하는 좋은 방법이 있습니까? JSONDecoder는 Decimal 객체를 허용하지 않으며 사전에 float로 변환하면 {'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 이상은 기본적으로 Decimal 유형을 지원합니다.

>>> 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'

이 기능이 표준 라이브러리에 포함되기를 바랍니다.


답변

Python 2.6.5를 실행하는 웹 서버에서 Michał Marczyk의 답변을 시도했지만 정상적으로 작동했음을 모든 사람에게 알리고 싶습니다. 그러나 Python 2.7로 업그레이드하면 작동이 중지되었습니다. Decimal 객체를 인코딩하는 방법을 생각해 보았습니다. 이것은 내가 생각해 낸 것입니다.

import decimal

class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, decimal.Decimal):
            return float(o)
        return super(DecimalEncoder, self).default(o)

파이썬 2.7에 문제가있는 사람이라면 누구나 도움이 될 것입니다. 나는 그것을 테스트했고 잘 작동하는 것 같다. 누군가 내 솔루션의 버그를 발견하거나 더 나은 방법을 찾은 경우 알려주십시오.


답변

Python 2.7.11, 플라스크 연금술 ( ‘db.decimal’유형) 및 Flask Marshmallow ( ‘instant’serializer 및 deserializer의 경우)를 사용하는 Flask 앱에서 GET 또는 POST를 수행 할 때 마다이 오류가 발생했습니다. . serializer 및 deserializer가 Decimal 형식을 JSON 식별 가능한 형식으로 변환하지 못했습니다.

“pip install simplejson”을 수행 한 다음

import simplejson as json

시리얼 라이저와 디시리얼라이저가 다시 시작됩니다. 나는 아무것도하지 않았다 … DEciamls는 ‘234.00’플로트 형식으로 표시됩니다.


답변

GAE 2.7에서 simplejson에서 내장 json으로 전환하려고 시도했으며 소수에 문제가 있습니다. default가 str (o)를 반환하면 _iterencode가 default 결과로 _iterencode를 호출하기 때문에 따옴표가 있었고 float (o)는 후행 0을 제거합니다.

기본값이 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]'


답변

기본 옵션이 누락되었으므로 다음 사람 / 공을 위해 추가 할 것입니다.

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)

프레스토 악장!


답변

내 $ .02!

웹 서버에 대한 수많은 데이터를 직렬화하기 때문에 JSON 인코더를 확장합니다. 좋은 코드가 있습니다. 그것은 당신이 느끼는 거의 모든 데이터 형식으로 쉽게 확장 가능하며 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

내 인생을 훨씬 더 쉽게 만듭니다 …