Eu tenho um Decimal('3.9')
como parte de um objeto, e gostaria de codificar isso para uma string JSON que deve se parecer {'x': 3.9}
. Eu não me importo com precisão no lado do cliente, então um float é bom.
Existe uma boa maneira de serializar isso? JSONDecoder não aceita objetos decimais, e a conversão para um float anteriormente gera o {'x': 3.8999999999999999}
que está errado e será um grande desperdício de largura de banda.
python
json
floating-point
decimal
Knio
fonte
fonte
Respostas:
Que tal subclassificar
json.JSONEncoder
?Em seguida, use-o assim:
fonte
DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
retornou o correto'3.9'
, masDecimalEncoder()._iterencode(3.9).next()
retornou um objeto gerador que só retornaria'3.899...'
quando você empilhasse em outro.next()
. Engraçado negócio de gerador. Oh bem ... Deve funcionar agora.return (str(o),)
vez disso?[o]
é uma lista com apenas 1 elemento, por que se preocupar em repetir isso?return (str(o),)
voltaria tupla de comprimento 1, enquanto o código na resposta retornos gerador de comprimento 1. Veja a iterencode () docsO Simplejson 2.1 e superior tem suporte nativo para o tipo Decimal:
Observe que isso
use_decimal
éTrue
por padrão:Assim:
Felizmente, esse recurso será incluído na biblioteca padrão.
fonte
json.loads(s, use_decimal=True)
ela retornará o decimal. Nenhuma flutuação em todo o processo. Editado acima da resposta. O cartaz original da esperança é bom com ele.use_decimal=True
as cargas.json.dumps({'a' : Decimal('3.9')}, use_decimal=True)
dá'{"a": 3.9}'
. O objetivo não era'{"a": "3.9"}'
?simplejson.dumps(decimal.Decimal('2.2'))
também funciona: não explícitouse_decimal
(testado no simplejson / 3.6.0). Outra maneira de carregá-lo de volta é:json.loads(s, parse_float=Decimal)
ou seja, você pode lê-lo usando stdlibjson
(esimplejson
versões antigas também são suportadas).Gostaria de informar a todos que tentei a resposta de Michał Marczyk no meu servidor Web que estava executando o Python 2.6.5 e funcionou bem. No entanto, atualizei para o Python 2.7 e ele parou de funcionar. Eu tentei pensar em algum tipo de maneira de codificar objetos decimais e é isso que eu criei:
Espero que isso ajude quem está tendo problemas com o Python 2.7. Eu testei e parece funcionar bem. Se alguém perceber algum erro na minha solução ou apresentar uma maneira melhor, informe-me.
fonte
unicode
ou emstr
vez defloat
para garantir precisão."54.4"
, não como um número.No meu aplicativo Flask, que usa python 2.7.11, alquimia de frascos (com tipos 'db.decimal') e Flask Marshmallow (para serializador e desserializador 'instantâneos'), eu tinha esse erro sempre que fazia um GET ou POST . O serializador e o desserializador falharam ao converter tipos decimais em qualquer formato identificável JSON.
Eu fiz um "pip install simplejson" e, em seguida, apenas adicionando
o serializador e o desserializador começam a ronronar novamente. Eu não fiz mais nada ... DEciamls são exibidos como formato flutuante '234.00'.
fonte
simplejson
- basta instalá-lo. Mencionado inicialmente por esta resposta .Decimal('0.00') is not JSON serializable
após a instalação via pip. Esta situação é quando você está usando marshmallow e grafeno. Quando uma consulta é chamada em uma API restante, o marshmallow funciona de maneira esperada para campos decimais. No entanto, quando é chamado com graphql, gera umis not JSON serializable
erro.Tentei mudar do simplejson para o json embutido no GAE 2.7 e tive problemas com o decimal. Se o padrão retornasse str (o), havia aspas (porque _iterencode chama _iterencode nos resultados do padrão) e float (o) removeria o zero final.
Se o padrão retornar um objeto de uma classe que herda de float (ou qualquer coisa que chame repr sem formatação adicional) e tenha um método __repr__ personalizado, ele parece funcionar como eu quero.
fonte
float.__repr__
codificado (que perde a precisão), efakefloat.__repr__
não é chamado de todo. A solução acima funciona corretamente para python3 até 3.5.1, se fakefloat tiver um método adicionaldef __float__(self): return self
.A opção nativa está faltando, então eu a adicionarei para o próximo cara / galha que a procurar.
A partir do Django 1.7.x, há um built-in do
DjangoJSONEncoder
qual você pode obtê-lodjango.core.serializers.json
.Presto!
fonte
Meu $ .02!
Eu estendo um monte de codificador JSON desde que estou serializando toneladas de dados para o meu servidor web. Aqui está um bom código. Observe que é facilmente extensível a praticamente qualquer formato de dados que você desejar e reproduz 3.9 como
"thing": 3.9
Torna minha vida muito mais fácil ...
fonte
"thing": "3.9"
.3.9
não pode ser representado exatamente nos carros alegóricos IEEE, sempre virá como3.8999999999999999
, por exemploprint repr(3.9)
, tente , você pode ler mais sobre isso aqui:http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Portanto, se você não quiser flutuar, apenas a opção deve ser enviada como string e para permitir a conversão automática de objetos decimais em JSON, faça algo assim:
fonte
json.loads("3.9")
vai funcionar, e eu gostaria que fosse isso #Para usuários do Django :
Recentemente, deparei com a
TypeError: Decimal('2337.00') is not JSON serializable
codificação JSON, ou seja,json.dumps(data)
Solução :
Mas agora o valor decimal será uma string, agora podemos definir explicitamente o analisador de valor decimal / flutuante ao decodificar dados, usando a
parse_float
opção emjson.loads
:fonte
No documento padrão JSON , conforme vinculado em json.org :
Portanto, é realmente preciso representar decimais como números (em vez de cadeias) em JSON. Abaixo está uma possível solução para o problema.
Defina um codificador JSON customizado:
Em seguida, use-o ao serializar seus dados:
Conforme observado nos comentários sobre as outras respostas, as versões mais antigas do python podem atrapalhar a representação ao converter para float, mas esse não é mais o caso.
Para recuperar o decimal em Python:
Esta solução é sugerida na documentação do Python 3.0 em decimais :
fonte
float
necessariamente faz você perder a representação decimal, e vai levar a discrepâncias. SeDecimal
é importante usar, acho melhor usar strings.Isto é o que eu tenho, extraído da nossa classe
Que passa mais unittest:
fonte
json.loads(myString, cls=CommonJSONEncoder)
comentário deve serjson.loads(myString, cls=CommonJSONDecoder)
Você pode criar um codificador JSON customizado conforme sua exigência.
O decodificador pode ser chamado assim,
e a saída será:
fonte
Para aqueles que não querem usar uma biblioteca de terceiros ... Um problema com a resposta de Elias Zamaria é que ele se converte em flutuante, o que pode gerar problemas. Por exemplo:
O
JSONEncoder.encode()
método permite retornar o conteúdo literal do json, ao contrário doJSONEncoder.default()
que você devolve um tipo compatível com o json (como float) que é codificado da maneira normal. O problemaencode()
é que (normalmente) funciona apenas no nível superior. Mas ainda é utilizável, com um pouco de trabalho extra (python 3.x):O que lhe dá:
fonte
Com base na resposta stdOrgnlDave , defini este wrapper que pode ser chamado com tipos opcionais, para que o codificador funcione apenas para certos tipos dentro de seus projetos. Eu acredito que o trabalho deve ser feito dentro do seu código e não para usar esse codificador "padrão", pois "é melhor explícito do que implícito", mas eu entendo que isso poupará seu tempo. :-)
fonte
Se você deseja passar um dicionário contendo casas decimais para a
requests
biblioteca (usando ojson
argumento de palavra - chave), basta instalarsimplejson
:O motivo do problema é que ele é
requests
usadosimplejson
apenas se estiver presente e volta ao incorporadojson
se não estiver instalado.fonte
isso pode ser feito adicionando
no
\Lib\json\encoder.py:JSONEncoder._iterencode
, mas eu estava esperando por uma solução melhorfonte