Ich habe ein Decimal('3.9')
als Teil eines Objekts und möchte dies in eine JSON-Zeichenfolge codieren, die aussehen sollte {'x': 3.9}
. Präzision auf der Client-Seite ist mir egal, daher ist ein Float in Ordnung.
Gibt es eine gute Möglichkeit, dies zu serialisieren? JSONDecoder akzeptiert keine Dezimalobjekte, und die vorherige Konvertierung in einen Float ergibt {'x': 3.8999999999999999}
eine falsche Leistung und ist eine große Verschwendung von Bandbreite.
Antworten:
Wie wäre es mit Unterklassen
json.JSONEncoder
?Dann benutze es so:
quelle
DecimalEncoder()._iterencode(decimal.Decimal('3.9')).next()
das korrekte zurückgegeben wurde'3.9'
, aberDecimalEncoder()._iterencode(3.9).next()
ein Generatorobjekt zurückgegeben wurde, das nur zurückgegeben wurde,'3.899...'
wenn Sie ein anderes gestapelt hatten.next()
. Generator lustiges Geschäft. Na ja ... sollte jetzt funktionieren.return (str(o),)
stattdessen?[o]
ist eine Liste mit nur 1 Element, warum sollte man sich die Mühe machen, sie zu durchlaufen?return (str(o),)
würde Tupel der Länge 1 zurückgeben, während Code in der Antwort Generator der Länge 1 zurückgibt.Simplejson 2.1 und höher bietet native Unterstützung für den Dezimaltyp:
Beachten Sie, dass
use_decimal
istTrue
standardmäßig:So:
Hoffentlich wird diese Funktion in der Standardbibliothek enthalten sein.
quelle
json.loads(s, use_decimal=True)
Sie die Dezimalstelle zurück. Kein Float im gesamten Prozess. Oben Antwort bearbeitet. Hoffe, das Originalplakat ist in Ordnung damit.use_decimal=True
die Lasten auch nicht benutzt.json.dumps({'a' : Decimal('3.9')}, use_decimal=True)
gibt'{"a": 3.9}'
. War das Ziel nicht'{"a": "3.9"}'
?simplejson.dumps(decimal.Decimal('2.2'))
funktioniert auch: nicht explizituse_decimal
(getestet auf simplejson / 3.6.0). Eine andere Möglichkeit, es zurückzuladen, ist:json.loads(s, parse_float=Decimal)
Sie können es mit stdlib lesenjson
(und altesimplejson
Versionen werden ebenfalls unterstützt).Ich möchte alle wissen lassen, dass ich Michał Marczyks Antwort auf meinem Webserver ausprobiert habe, auf dem Python 2.6.5 ausgeführt wurde, und dass es einwandfrei funktioniert hat. Ich habe jedoch ein Upgrade auf Python 2.7 durchgeführt und es funktioniert nicht mehr. Ich habe versucht, mir eine Möglichkeit zum Codieren von Dezimalobjekten auszudenken, und mir ist Folgendes eingefallen:
Dies sollte hoffentlich jedem helfen, der Probleme mit Python 2.7 hat. Ich habe es getestet und es scheint gut zu funktionieren. Wenn jemand Fehler in meiner Lösung bemerkt oder einen besseren Weg findet, lassen Sie es mich bitte wissen.
quelle
unicode
oderstr
anstelle vonfloat
, um Präzision zu gewährleisten."54.4"
und nicht als eine Zahl.In meiner Flask-App, die Python 2.7.11, Flask-Alchemie (mit 'db.decimal'-Typen) und Flask Marshmallow (für' Instant'-Serializer und -Deserializer) verwendet, trat dieser Fehler jedes Mal auf, wenn ich einen GET oder POST durchführte . Der Serializer und Deserializer konnte keine Dezimaltypen in ein JSON-identifizierbares Format konvertieren.
Ich habe eine "pip install simplejson" gemacht, dann einfach durch Hinzufügen
Der Serializer und Deserializer beginnt wieder zu schnurren. Ich habe nichts anderes getan ... DEciamls werden als Float-Format '234.00' angezeigt.
quelle
simplejson
- nur die Installation reicht aus. Zunächst durch diese Antwort erwähnt .Decimal('0.00') is not JSON serializable
nach der Installation über pip immer noch angezeigt. Diese Situation ist, wenn Sie sowohl Marshmallow als auch Graphen verwenden. Wenn eine Abfrage für eine Rest-API aufgerufen wird, funktioniert Marshmallow erwartungsgemäß für Dezimalfelder. Wenn es jedoch mit graphql aufgerufen wird, wird einis not JSON serializable
Fehler ausgelöst .Ich habe versucht, für GAE 2.7 von simplejson auf integriertes json umzuschalten, und hatte Probleme mit der Dezimalstelle. Wenn default str (o) zurückgibt, gibt es Anführungszeichen (weil _iterencode _iterencode für die Ergebnisse von default aufruft), und float (o) würde die nachfolgende 0 entfernen.
Wenn default ein Objekt einer Klasse zurückgibt, die von float erbt (oder etwas, das repr ohne zusätzliche Formatierung aufruft) und eine benutzerdefinierte __repr__ -Methode hat, scheint es so zu funktionieren, wie ich es möchte.
quelle
float.__repr__
codiert (das verliert an Präzision) undfakefloat.__repr__
wird überhaupt nicht aufgerufen. Die obige Lösung funktioniert ordnungsgemäß für Python3 bis 3.5.1, wenn Fakefloat über eine zusätzliche Methode verfügtdef __float__(self): return self
.Die native Option fehlt, also füge ich sie für den nächsten Kerl / die nächste Galle hinzu, die danach sucht.
Ab Django 1.7.x gibt es eine integrierte Funktion
DjangoJSONEncoder
, von der Sie sie erhalten könnendjango.core.serializers.json
.Presto!
quelle
Meine $ .02!
Ich erweitere eine Reihe von JSON-Encodern, da ich Tonnen von Daten für meinen Webserver serialisiere. Hier ist ein netter Code. Beachten Sie, dass es leicht auf so ziemlich jedes Datenformat erweiterbar ist, das Sie möchten, und 3.9 als reproduziert
"thing": 3.9
Macht mein Leben so viel einfacher ...
quelle
"thing": "3.9"
.3.9
kann nicht genau in IEEE-Floats dargestellt werden, es wird immer so kommen3.8999999999999999
, zB versuchenprint repr(3.9)
Sie hier mehr darüber zu lesen:http://en.wikipedia.org/wiki/Floating_point
http://docs.sun.com/source/806-3568/ncg_goldberg.html
Wenn Sie also kein Float möchten, müssen Sie es nur als Zeichenfolge senden und die automatische Konvertierung von Dezimalobjekten in JSON ermöglichen. Gehen Sie folgendermaßen vor:
quelle
json.loads("3.9")
wird funktionieren, und ich möchte, dass es so istFür Django-Benutzer :
Vor kurzem kam
TypeError: Decimal('2337.00') is not JSON serializable
während JSON-Codierung dhjson.dumps(data)
Lösung :
Aber jetzt ist der Dezimalwert eine Zeichenfolge. Jetzt können wir den Dezimal- / Gleitkomma-Parser beim Dekodieren von Daten explizit festlegen, indem wir die
parse_float
Option in verwendenjson.loads
:quelle
Aus dem JSON-Standarddokument , wie in json.org verlinkt :
Es ist also tatsächlich korrekt, Dezimalstellen in JSON als Zahlen (anstatt als Zeichenfolgen) darzustellen. Unten liegt eine mögliche Lösung für das Problem.
Definieren Sie einen benutzerdefinierten JSON-Encoder:
Verwenden Sie es dann beim Serialisieren Ihrer Daten:
Wie aus den Kommentaren zu den anderen Antworten hervorgeht, können ältere Versionen von Python die Darstellung beim Konvertieren in Float durcheinander bringen, aber das ist nicht mehr der Fall.
So erhalten Sie die Dezimalstelle in Python zurück:
Diese Lösung wird in der Python 3.0-Dokumentation zu Dezimalstellen angedeutet :
quelle
float
notwendigerweise machen Sie die Dezimaldarstellung verlieren, und werden zu Abweichungen führen. WennDecimal
es wichtig ist, Zeichenfolgen zu verwenden, ist es meiner Meinung nach besser, Zeichenfolgen zu verwenden.Das habe ich aus unserer Klasse extrahiert
Was unittest vergeht:
quelle
json.loads(myString, cls=CommonJSONEncoder)
Kommentar sollte seinjson.loads(myString, cls=CommonJSONDecoder)
Sie können einen benutzerdefinierten JSON-Encoder gemäß Ihren Anforderungen erstellen.
Der Decoder kann so aufgerufen werden,
und die Ausgabe wird sein:
quelle
Für diejenigen, die keine Bibliothek eines Drittanbieters verwenden möchten ... Ein Problem mit der Antwort von Elias Zamaria ist, dass sie in Float konvertiert wird, was zu Problemen führen kann. Beispielsweise:
Mit dieser
JSONEncoder.encode()
Methode können Sie den wörtlichen json-Inhalt zurückgeben, im Gegensatz dazuJSONEncoder.default()
, dass Sie einen json-kompatiblen Typ (wie float) zurückgeben, der dann auf normale Weise codiert wird. Das Problem dabeiencode()
ist, dass es (normalerweise) nur auf der obersten Ebene funktioniert. Aber es ist immer noch verwendbar, mit ein wenig zusätzlicher Arbeit (Python 3.x):Welches gibt Ihnen:
quelle
Basierend auf der Antwort von stdOrgnlDave habe ich diesen Wrapper so definiert, dass er mit optionalen Arten aufgerufen werden kann, sodass der Encoder nur für bestimmte Arten in Ihren Projekten funktioniert. Ich glaube, die Arbeit sollte in Ihrem Code erledigt werden und nicht diesen "Standard" -Codierer zu verwenden, da "es besser explizit als implizit ist", aber ich verstehe, dass die Verwendung dieses Encoders einen Teil Ihrer Zeit spart. :-)
quelle
Wenn Sie ein Wörterbuch mit Dezimalstellen an die
requests
Bibliothek übergeben möchten (mithilfe desjson
Schlüsselwortarguments), müssen Sie lediglich Folgendes installierensimplejson
:Der Grund für das Problem ist, dass es nur
requests
verwendet wird,simplejson
wenn es vorhanden ist, und auf das integrierte System zurückgreift,json
wenn es nicht installiert ist.quelle
Dies kann durch Hinzufügen erfolgen
in
\Lib\json\encoder.py:JSONEncoder._iterencode
, aber ich hoffte auf eine bessere Lösungquelle