У меня есть Decimal('3.9')
как часть объекта, и я хочу закодировать это в строку JSON, которая должна выглядеть следующим образом {'x': 3.9}
. Меня не волнует точность на стороне клиента, поэтому с плавающей точкой все в порядке.
Есть ли хороший способ сериализовать это? JSONDecoder не принимает десятичные объекты, и предварительное преобразование в число с плавающей запятой дает {'x': 3.8999999999999999}
неправильный результат, и это приведет к большой трате пропускной способности.
Ответы:
Как насчет подклассов
json.JSONEncoder
?Тогда используйте это так:
источник
DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
возвращался правильный'3.9'
, ноDecimalEncoder()._iterencode(3.9).next()
возвращался объект генератора, который возвращался только'3.899...'
тогда, когда вы наваливали другой.next()
. Генератор забавного бизнеса. Ну хорошо ... Должно работать сейчас.return (str(o),)
вместо этого?[o]
список только с одним элементом, зачем его зацикливать?return (str(o),)
вернет кортеж длины 1, а код в ответе вернет генератор длины 1. См. Документы iterencode ()Simplejson 2.1 и выше имеет встроенную поддержку типа Decimal:
Обратите внимание , что
use_decimal
этоTrue
по умолчанию:Так:
Надеюсь, эта функция будет включена в стандартную библиотеку.
источник
json.loads(s, use_decimal=True)
вы получите десятичную. Не плавать во всем процессе. Отредактированный выше ответ. Надеюсь, оригинальный плакат хорошо с ним.use_decimal=True
на нагрузках.json.dumps({'a' : Decimal('3.9')}, use_decimal=True)
дает'{"a": 3.9}'
. Была ли цель не'{"a": "3.9"}'
?simplejson.dumps(decimal.Decimal('2.2'))
также работает: без явногоuse_decimal
(проверено на simplejson / 3.6.0). Другой способ загрузить его обратно:json.loads(s, parse_float=Decimal)
вы можете прочитать его, используя stdlibjson
(и старыеsimplejson
версии также поддерживаются).Я хотел бы, чтобы все знали, что я попробовал ответ Михаила Марчика на своем веб-сервере, на котором работал Python 2.6.5, и он работал нормально. Однако я обновился до Python 2.7, и он перестал работать. Я попытался придумать какой-нибудь способ кодирования десятичных объектов, и вот что я придумал:
Надеюсь, это поможет всем, у кого проблемы с Python 2.7. Я проверил это, и, кажется, работает нормально. Если кто-то заметит какие-либо ошибки в моем решении или найдет лучший способ, пожалуйста, дайте мне знать.
источник
unicode
илиstr
вместо того,float
чтобы обеспечить точность."54.4"
, а не число.В моем приложении Flask, которое использует python 2.7.11, алхимия фляг (с типами 'db.decimal) и Flask Marshmallow (для сериализатора и десериализатора' instant '), у меня была эта ошибка, каждый раз, когда я делал GET или POST , Сериализатору и десериализатору не удалось преобразовать десятичные типы в любой идентифицируемый формат JSON.
Я сделал "pip install simplejson", затем просто добавив
Сериализатор и десериализатор снова начинают мурлыкать. Я больше ничего не делал ... DEciamls отображаются в формате '234.00'.
источник
simplejson
- просто установка делает свое дело. Первоначально упоминается этот ответ .Decimal('0.00') is not JSON serializable
после установки его через pip. Это ситуация, когда вы используете и зефир, и графен. Когда запрос вызывается для API остальных, зефир работает ожидаемо для десятичных полей. Однако, когда он вызывается с graphql, возникаетis not JSON serializable
ошибка.Я попытался переключиться с simplejson на встроенный json для GAE 2.7, и у меня были проблемы с десятичной дробью. Если default вернул str (o), то были кавычки (потому что _iterencode вызывает _iterencode по результатам по умолчанию), а float (o) удалит завершающий 0.
Если default возвращает объект класса, который наследуется от float (или что-либо, что вызывает repr без дополнительного форматирования) и имеет собственный метод __repr__, похоже, он работает так, как я хочу.
источник
float.__repr__
жестко закодирован (что теряет точность) иfakefloat.__repr__
не вызывается вообще. Приведенное выше решение работает правильно для python3 до 3.5.1, если в fakefloat есть дополнительный методdef __float__(self): return self
.Нативный вариант отсутствует, поэтому я добавлю его для следующего парня / галла, который его ищет.
Начиная с Django 1.7.x, есть встроенный модуль, из
DjangoJSONEncoder
которого вы можете получить егоdjango.core.serializers.json
.Presto!
источник
Мои $ .02!
Я расширяю связку JSON-кодировщика, поскольку сериализую тонны данных для своего веб-сервера. Вот хороший код. Обратите внимание, что он легко расширяется практически на любой формат данных, который вам нравится, и будет воспроизводить 3.9 как
"thing": 3.9
Делает мою жизнь намного проще ...
источник
"thing": "3.9"
.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, сделайте что-то вроде этого:
источник
json.loads("3.9")
будет работать, и я хотел бы, чтобы это былоДля пользователей Django :
Недавно наткнулся,
TypeError: Decimal('2337.00') is not JSON serializable
пока кодировка JSON т.е.json.dumps(data)
Решение :
Но теперь десятичное значение будет строкой, теперь мы можем явно установить анализатор десятичных / плавающих значений при декодировании данных, используя
parse_float
параметр вjson.loads
:источник
Из стандартного документа JSON , как указано в json.org :
Так что на самом деле точно представить десятичные числа в виде чисел (а не строк) в JSON. Ниже приводится возможное решение проблемы.
Определите пользовательский кодер JSON:
Затем используйте его при сериализации ваших данных:
Как отмечалось в комментариях к другим ответам, старые версии python могут испортить представление при конвертации в float, но это уже не так.
Чтобы вернуть десятичное число в Python:
Это решение упоминается в документации Python 3.0 о десятичных дробях :
источник
float
обязательно заставляет вас терять десятичное представление, и будет приводить к несоответствиям. ЕслиDecimal
важно использовать, я думаю, что лучше использовать строки.Это то, что я извлек из нашего класса
Который проходит unittest:
источник
json.loads(myString, cls=CommonJSONEncoder)
комментарий должен бытьjson.loads(myString, cls=CommonJSONDecoder)
Вы можете создать собственный кодер JSON в соответствии с вашими требованиями.
Декодер можно назвать так,
и вывод будет:
источник
Для тех, кто не хочет использовать стороннюю библиотеку ... Проблема с ответом Элиаса Замарии заключается в том, что она конвертируется в плавающую, что может привести к проблемам. Например:
Этот
JSONEncoder.encode()
метод позволяет вам возвращать буквальное содержимое json, в отличие отJSONEncoder.default()
которого вы возвращаете json-совместимый тип (например, float), который затем кодируется обычным способом. Проблема вencode()
том, что он (обычно) работает только на верхнем уровне. Но это все еще удобно, с небольшой дополнительной работой (python 3.x):Что дает вам:
источник
Основываясь на ответе stdOrgnlDave, я определил эту оболочку, чтобы ее можно было вызывать с необязательными типами, чтобы кодировщик работал только для определенных типов внутри ваших проектов. Я считаю, что работа должна выполняться внутри вашего кода, а не использовать этот кодировщик «по умолчанию», поскольку «он лучше явный, чем неявный», но я понимаю, что использование этого сэкономит ваше время. :-)
источник
Если вы хотите передать в
requests
библиотеку словарь, содержащий десятичные дроби (используяjson
ключевое слово аргумент), вам просто нужно установитьsimplejson
:Причиной проблемы является то, что
requests
используетsimplejson
только, если он присутствует, и возвращается к встроенному,json
если он не установлен.источник
это можно сделать, добавив
в
\Lib\json\encoder.py:JSONEncoder._iterencode
, но я надеялся на лучшее решениеисточник