json: float gerundet auf zwei Stellen hinterm Komma...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich möchte gern JSON erzeugen. Dabei sollen float Zahlen auf der zweiten Stelle hinter dem Komma gerundet werden.

So einfach scheint das aber nicht zu gehen. Ich sehe keine Möglichkeit das Format der bekannten Datentypen zu ändern. Man kann anscheinent die Ausgabe nur bei unbekannten Objekten beeinflussen.

Also nehme ich Decimal. Denn das kann JSON normalerweise nicht.

Code: Alles auswählen

import decimal
import json

class WeaveJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, decimal.Decimal):
            #~ return "%.2f" % round(obj, 2) # -> "1.19"
            return round(obj, 2) # -> 1.1899999999999999

        return super(WeaveJSONEncoder, self).default(obj)


data = {"foo":decimal.Decimal('1.19'), "bar": 2.19}
print json.dumps(data, cls=WeaveJSONEncoder)
Wenn ich return round(obj, 2) mache, kommt das raus:

Code: Alles auswählen

{"foo": 1.1899999999999999, "bar": 2.1899999999999999
Ist auch klar. Decimal wird ja dann wieder zu einem float...

Mit return "%.2f" % round(obj, 2) erhalte ich aber Strings in JSON und keine Zahl mehr:

Code: Alles auswählen

{"foo": "1.19", "bar": "2.19"}
Das ist das Ziel:

Code: Alles auswählen

{"foo": 1.19, "bar": 2.19}

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab nun was:

Code: Alles auswählen

import decimal
import json

class RoundedFloat(object):
    def __init__(self, value, places):
        self.value = value
        self.places = places

    def __str__(self):
        fmt = "%%.%sf" % self.places
        return fmt % round(self.value, self.places)

class WeaveJSONEncoder(json.JSONEncoder):
    def _iterencode_default(self, obj, markers=None):
        if isinstance(obj, RoundedFloat):
            return [str(obj)]
        else:
            return super(WeaveJSONEncoder, self)._iterencode_default(o, markers=markers)


data = {"foo":RoundedFloat(1.19, 2), "bar": RoundedFloat(1.19, 3)}
print json.dumps(data, cls=WeaveJSONEncoder)
Ausgabe:

Code: Alles auswählen

{"foo": 1.19, "bar": 1.190}
Aber schon recht umständlich...

EDIT: Sourcen aktualisiert.
Zeilen 10 + 11 sind unschön...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Das sieht arg nach "Kosmetik" aus, denn wenn Du das wieder einliest bekommst Du ja wieder die "ungenauen" floats.
Darii
User
Beiträge: 1177
Registriert: Donnerstag 29. November 2007, 17:02

jens hat geschrieben:Ich möchte gern JSON erzeugen. Dabei sollen float Zahlen auf der zweiten Stelle hinter dem Komma gerundet werden.

So einfach scheint das aber nicht zu gehen. Ich sehe keine Möglichkeit das Format der bekannten Datentypen zu ändern. Man kann anscheinent die Ausgabe nur bei unbekannten Objekten beeinflussen.
Du kannst die Funktion json.encode.floatstr durch eine eigene ersetzen(monkey patch).
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vielleicht sind floats dann evtl. doch nicht die richtige Darstellungsform? Worum geht es denn konkret? Preise?
lunar

@jens: Fließkommazahlen sollte man nur bei der Anzeige runden, nicht aber intern (sei es bei Berechnungen oder beim Datentransfer).
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich weiß nicht genau, ob ich das auch wirklich brauche ;)

Es geht um http://code.google.com/p/django-weave/

Der Mozilla Weave Server sendet JSON mit Timestamps, die nur zwei Stellen hinter dem Komma haben. Ich kann noch nicht mir Sicherheit sagen, ob das Firefox Addon mit mehr Stellen klar kommt.

Mit ist noch was aufgefallen. Der Mozilla Server maskiert in JSON alle Slashes (z.B. in URLs) mit Backslashes. Der Python JSON Encoder macht das nicht. Ob es einen Unterschied macht, weiß ich auch noch nicht.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Firefox wird das doch in JavaScript parsen!? Die Fliesskommazahlen sind doch da genau so "ungenau", d.h. wer auch immer das JSON parst bekommt aus 1.19 eine Fliesskommazahl die *so* nicht intern als IEE7irgendwas repräsentiert werden kann.

Und warum sollten die Backslashes Probleme machen? Das wird als JSON geparst und in JavaScript hat der Backslash in Zeichenketten doch auch so eine Bedeutung wie in Python.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Nutzt das json Modul einfach und wenn es ein Problem gibt mach ein Ticket auf. Willst du jetzt auch noch die JSON Spezifikation lesen um sicherzustellen dass da auch jeder eine Bibliothek nutzt die die richtig umsetzt?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das Problem ist, das der sync noch nicht klappt. Um alle Fehlerquellen auszuschließen, habe ich meinen json response mit dem vom mozilla server verglichen und die Unterschiede gesehen.

Das runden der Zeiten, kommt ursprünglich aus anderen Python implementationen. Deswegen hab ich das übernommen.

So mache ich es z.Z.:

Code: Alles auswählen

def float_repr(f):
    return "%.2f" % round(f, 2)
json.encoder.FLOAT_REPR = float_repr
http://code.google.com/p/django-weave/s ... orators.py

Was bisher ein wenig Klappt ist das Syncronisieren der History und der Tabs. Bookmarks kommt zwar auch irgendwie an, aber nicht richtig eingeordnet.

Das Problem ist das Debuggen. Die API ist zwar dokumentiert: https://wiki.mozilla.org/Labs/Weave/API aber viele Fragen lässt es offen.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:So mache ich es z.Z.:

Code: Alles auswählen

def float_repr(f):
    return "%.2f" % round(f, 2)
json.encoder.FLOAT_REPR = float_repr
http://code.google.com/p/django-weave/s ... orators.py
Also das Monkeypatching ist ja gruselig für jeden der deinen Code als Lib einbinden will, der wundert sich dann ggf warum die Floats so fürchterlich falsch serialisiert werden.
jens hat geschrieben:Das Problem ist das Debuggen. Die API ist zwar dokumentiert: https://wiki.mozilla.org/Labs/Weave/API aber viele Fragen lässt es offen.
Und die Mozilla-Leute sind sicherlich nicht über IRC erreichbar um dort mal nachzufragen, statt herumzuraten?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Leonidas hat geschrieben:Und die Mozilla-Leute sind sicherlich nicht über IRC erreichbar um dort mal nachzufragen, statt herumzuraten?
Ich glaub, die gehen früh ins Bett.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Der Monkeypatch ist nur eine Notlösung und sollte nicht ins "Endprodukt"...

Btw. das Abfragen mit dem Python Client funktioniert:
https://wiki.mozilla.org/Weave/Experime ... nts/Python

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten