Tengo un Decimal('3.9')
como parte de un objeto, y deseo codificar esto en una cadena JSON que debería verse así {'x': 3.9}
. No me importa la precisión en el lado del cliente, por lo que un flotador está bien.
¿Hay una buena manera de serializar esto? JSONDecoder no acepta objetos decimales, y la conversión a un flotante de antemano produce lo {'x': 3.8999999999999999}
que está mal, y será un gran desperdicio de ancho de banda.
Respuestas:
¿Qué tal subclases
json.JSONEncoder
?Luego úsalo así:
fuente
DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
devolvió el correcto'3.9'
, peroDecimalEncoder()._iterencode(3.9).next()
devolvió un objeto generador que solo regresaría'3.899...'
cuando se acumulaba en otro.next()
. Generador de negocios divertidos. Oh bueno ... Debería funcionar ahora.return (str(o),)
lugar?[o]
es una lista con solo 1 elemento, ¿por qué molestarse en recorrerlo?return (str(o),)
devolvería una tupla de longitud 1, mientras que el código en la respuesta devuelve el generador de longitud 1. Ver los documentos iterencode ()Simplejson 2.1 y superior tiene soporte nativo para tipo Decimal:
Tenga en cuenta que
use_decimal
esTrue
por defecto:Entonces:
Con suerte, esta característica se incluirá en la biblioteca estándar.
fuente
json.loads(s, use_decimal=True)
, le devuelve el decimal. Sin flotación en todo el proceso. Editado arriba de la respuesta. Espero que el póster original esté bien con él.use_decimal=True
en las cargas.json.dumps({'a' : Decimal('3.9')}, use_decimal=True)
da'{"a": 3.9}'
. ¿No fue el gol'{"a": "3.9"}'
?simplejson.dumps(decimal.Decimal('2.2'))
también funciona: no explícitouse_decimal
(probado en simplejson / 3.6.0). Otra forma de volver a cargarlo es: esjson.loads(s, parse_float=Decimal)
decir, puede leerlo usando stdlibjson
(ysimplejson
también se admiten versiones anteriores).Me gustaría que todos sepan que probé la respuesta de Michał Marczyk en mi servidor web que ejecutaba Python 2.6.5 y funcionó bien. Sin embargo, actualicé a Python 2.7 y dejó de funcionar. Traté de pensar en algún tipo de forma de codificar objetos Decimal y esto es lo que se me ocurrió:
Con suerte, esto debería ayudar a cualquiera que tenga problemas con Python 2.7. Lo probé y parece funcionar bien. Si alguien nota algún error en mi solución o encuentra una mejor manera, hágamelo saber.
fuente
unicode
o enstr
lugar defloat
para garantizar la precisión."54.4"
, no como un número.En mi aplicación Flask, que usa python 2.7.11, matraz alquimia (con tipos 'db.decimal') y Flask Marshmallow (para serializador y deserializador 'instantáneo'), tuve este error, cada vez que hice un GET o POST . El serializador y el deserializador no pudieron convertir los tipos decimales en ningún formato identificable JSON.
Hice un "pip install simplejson", luego simplemente agregando
el serializador y el deserializador comienzan a ronronear nuevamente. No hice nada más ... DEciamls se muestran como formato flotante '234.00'.
fuente
simplejson
, solo instalarlo hace el truco. Inicialmente mencionado por esta respuesta .Decimal('0.00') is not JSON serializable
después de instalarlo a través de pip. Esta situación es cuando estás usando malvavisco y grafeno. Cuando se llama a una consulta en una API de reposo, malvavisco funciona de forma esperada para los campos decimales. Sin embargo, cuando se llama con graphql, genera unis not JSON serializable
error.Intenté cambiar de simplejson a json incorporado para GAE 2.7, y tuve problemas con el decimal. Si default devolvió str (o) había comillas (porque _iterencode llama a _iterencode en los resultados de default), y float (o) eliminaría el 0 final.
Si el valor predeterminado devuelve un objeto de una clase que hereda de float (o cualquier cosa que llame a repr sin formato adicional) y tenga un método __repr__ personalizado, parece funcionar como yo quiero.
fuente
float.__repr__
código rígido (que pierde precisión) yfakefloat.__repr__
no se llama en absoluto. La solución anterior funciona correctamente para python3 hasta 3.5.1, si fakefloat tiene un método adicionaldef __float__(self): return self
.Falta la opción nativa, así que la agregaré para la próxima persona que la busque.
A partir de Django 1.7.x hay una función integrada desde la
DjangoJSONEncoder
que puede obtenerladjango.core.serializers.json
.¡Presto!
fuente
¡Mis $ .02!
Extiendo un montón del codificador JSON ya que estoy serializando toneladas de datos para mi servidor web. Aquí hay un buen código. Tenga en cuenta que es fácilmente extensible a casi cualquier formato de datos que desee y se reproducirá 3.9 como
"thing": 3.9
Hace mi vida mucho más fácil ...
fuente
"thing": "3.9"
.3.9
no se puede representar exactamente en los flotadores IEEE, siempre vendrá como3.8999999999999999
, por ejemploprint repr(3.9)
, intente , puede leer más sobre esto aquí:http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Entonces, si no desea la opción flotante, solo tiene que enviarla como una cadena y permitir la conversión automática de objetos decimales a JSON, haga algo como esto:
fuente
json.loads("3.9")
funcionará, y me gustaría que fuera asíPara usuarios de Django :
Recientemente me encontré con la
TypeError: Decimal('2337.00') is not JSON serializable
codificación JSON, es decirjson.dumps(data)
Solución :
Pero, ahora el valor decimal será una cadena, ahora podemos establecer explícitamente el analizador de valor decimal / flotante al decodificar datos, usando la
parse_float
opción enjson.loads
:fuente
Del documento estándar JSON , como se vincula en json.org :
Por lo tanto, en realidad es preciso representar los decimales como números (en lugar de cadenas) en JSON. A continuación se encuentra una posible solución al problema.
Defina un codificador JSON personalizado:
Luego úselo cuando serialice sus datos:
Como se señaló en los comentarios sobre las otras respuestas, las versiones anteriores de python podrían estropear la representación al convertir a flotante, pero ese ya no es el caso.
Para recuperar el decimal en Python:
Esta solución se insinúa en la documentación de Python 3.0 sobre decimales :
fuente
float
necesariamente te hace perder la representación decimal, y dará lugar a discrepancias. SiDecimal
es importante usarlo, creo que es mejor usar cadenas.Esto es lo que tengo, extraído de nuestra clase
Que pasa unittest:
fuente
json.loads(myString, cls=CommonJSONEncoder)
comentario debería serjson.loads(myString, cls=CommonJSONDecoder)
Puede crear un codificador JSON personalizado según sus necesidades.
El decodificador se puede llamar así,
y la salida será:
fuente
Para aquellos que no quieren usar una biblioteca de terceros ... Un problema con la respuesta de Elias Zamaria es que se convierte en flotante, lo que puede generar problemas. Por ejemplo:
El
JSONEncoder.encode()
método le permite devolver el contenido literal de json, a diferencia de loJSONEncoder.default()
que le permite devolver un tipo compatible con json (como flotante) que luego se codifica de la manera normal. El problemaencode()
es que (normalmente) solo funciona en el nivel superior. Pero aún es utilizable, con un poco de trabajo extra (python 3.x):Lo que te da:
fuente
Basado en la respuesta de stdOrgnlDave , he definido este contenedor que se puede llamar con tipos opcionales para que el codificador funcione solo para ciertos tipos dentro de sus proyectos. Creo que el trabajo debe hacerse dentro de su código y no usar este codificador "predeterminado" ya que "es mejor explícito que implícito", pero entiendo que usar esto le ahorrará algo de tiempo. :-)
fuente
Si desea pasar un diccionario que contiene decimales a la
requests
biblioteca (utilizando eljson
argumento de la palabra clave), simplemente necesita instalarsimplejson
:La razón del problema es que solo se
requests
usasimplejson
si está presente, y recurre al incorporadojson
si no está instalado.fuente
esto se puede hacer agregando
adentro
\Lib\json\encoder.py:JSONEncoder._iterencode
, pero esperaba una mejor soluciónfuente