Python JSON сериализует десятичный объект - PullRequest
191 голосов
/ 25 декабря 2009

У меня есть Decimal('3.9') как часть объекта, и я хочу закодировать это в строку JSON, которая должна выглядеть как {'x': 3.9}. Меня не волнует точность на стороне клиента, поэтому с плавающей точкой все в порядке.

Есть ли хороший способ сериализовать это? JSONDecoder не принимает десятичные объекты, и предварительное преобразование в число с плавающей запятой дает {'x': 3.8999999999999999}, что неверно и будет большой тратой пропускной способности.

Ответы [ 15 ]

3 голосов
/ 28 мая 2019

Для пользователей 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)
2 голосов
/ 17 января 2018

На основе ответа 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()
1 голос
/ 06 сентября 2018

Вы можете создать собственный кодер JSON в соответствии с вашими требованиями.

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)

Декодер можно назвать так,

import json
from decimal import Decimal
json.dumps({'x': Decimal('3.9')}, cls=CustomJSONEncoder)

и вывод будет:

>>'{"x": 3.9}'
0 голосов
/ 14 апреля 2018

Если вы хотите передать словарь, содержащий десятичные дроби, в библиотеку 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 использует simplejson, только если он присутствует, и возвращается к встроенному json, если он не установлен.

0 голосов
/ 25 декабря 2009

это можно сделать, добавив

    elif isinstance(o, decimal.Decimal):
        yield str(o)

в \Lib\json\encoder.py:JSONEncoder._iterencode, но я надеялся на лучшее решение

...