Ich habe einen Python set
, der Objekte mit __hash__
und __eq__
Methoden enthält , um sicherzustellen, dass keine Duplikate in der Sammlung enthalten sind.
Ich muss dieses Ergebnis json codieren set
, aber das Übergeben eines Leerzeichens set
an die json.dumps
Methode löst a aus TypeError
.
File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py", line 178, in default
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable
Ich weiß, dass ich eine Erweiterung für die json.JSONEncoder
Klasse erstellen kann , die eine benutzerdefinierte default
Methode hat, aber ich bin mir nicht einmal sicher, wo ich mit der Konvertierung über beginnen soll set
. Sollte ich aus den set
Werten der Standardmethode ein Wörterbuch erstellen und dann die Codierung dafür zurückgeben? Im Idealfall möchte ich die Standardmethode in die Lage versetzen, alle Datentypen zu verarbeiten, an denen der ursprüngliche Encoder erstickt (ich verwende Mongo als Datenquelle, sodass Daten diesen Fehler ebenfalls auslösen).
Jeder Hinweis in die richtige Richtung wäre willkommen.
BEARBEITEN:
Danke für die Antwort! Vielleicht hätte ich genauer sein sollen.
Ich habe die Antworten hier verwendet (und positiv bewertet), um die Einschränkungen der set
Übersetzung zu umgehen , aber es gibt auch interne Schlüssel, die ein Problem darstellen.
Die Objekte in set
sind komplexe Objekte, die übersetzt werden __dict__
, aber sie selbst können auch Werte für ihre Eigenschaften enthalten, die für die Basistypen im JSON-Encoder möglicherweise nicht geeignet sind.
Es gibt viele verschiedene Typen set
, und der Hash berechnet im Grunde eine eindeutige ID für die Entität, aber im wahren Geist von NoSQL kann nicht genau gesagt werden, was das untergeordnete Objekt enthält.
Ein Objekt enthält möglicherweise einen Datumswert für starts
, während ein anderes Objekt möglicherweise ein anderes Schema enthält, das keine Schlüssel enthält, die "nicht primitive" Objekte enthalten.
Deshalb ist die einzige Lösung , die ich denken konnte, war das zu erweitern , JSONEncoder
die ersetzen default
Methode auf verschiedene Fälle zu drehen - aber ich bin nicht sicher , wie dies zu realisieren und die Dokumentation ist nicht eindeutig. Wird in verschachtelten Objekten der von default
go zurückgegebene Wert per Schlüssel angegeben, oder handelt es sich nur um ein generisches Einschließen / Verwerfen, das das gesamte Objekt betrachtet? Wie berücksichtigt diese Methode verschachtelte Werte? Ich habe frühere Fragen durchgesehen und kann anscheinend nicht den besten Ansatz für die fallspezifische Codierung finden (was leider so aussieht, wie ich es hier tun muss).
quelle
dict
s? Ich denke, Sie möchten nur einlist
Set machen und es dann an den Encoder weitergeben ... zB:encode(list(myset))
Antworten:
Die JSON- Notation enthält nur eine Handvoll nativer Datentypen (Objekte, Arrays, Zeichenfolgen, Zahlen, Boolesche Werte und Null). Daher muss alles, was in JSON serialisiert wird, als einer dieser Typen ausgedrückt werden.
Wie in den Dokumenten des JSON-Moduls gezeigt , kann diese Konvertierung automatisch von einem JSONEncoder und JSONDecoder durchgeführt werden. Dann würden Sie jedoch eine andere Struktur aufgeben, die Sie möglicherweise benötigen (wenn Sie Sets in eine Liste konvertieren, verlieren Sie die Fähigkeit, regelmäßig wiederherzustellen Listen; wenn Sie Sets mit in ein Wörterbuch konvertieren,
dict.fromkeys(s)
verlieren Sie die Fähigkeit, Wörterbücher wiederherzustellen.Eine komplexere Lösung besteht darin, einen benutzerdefinierten Typ zu erstellen, der mit anderen nativen JSON-Typen koexistieren kann. Auf diese Weise können Sie verschachtelte Strukturen speichern, die Listen, Mengen, Diktate, Dezimalstellen, Datums- / Uhrzeitobjekte usw. enthalten.
Hier ist eine Beispielsitzung, die zeigt, dass sie Listen, Diktate und Mengen verarbeiten kann:
Alternativ kann es nützlich sein, eine allgemeinere Serialisierungstechnik wie YAML , Twisted Jelly oder Pythons Pickle-Modul zu verwenden . Diese unterstützen jeweils einen viel größeren Bereich von Datentypen.
quelle
JSONDecoder
, verwendet es aber nichtSie können einen benutzerdefinierten Encoder erstellen, der a zurückgibt,
list
wenn er auf a trifftset
. Hier ist ein Beispiel:Auf diese Weise können Sie auch andere Typen erkennen. Wenn Sie beibehalten möchten, dass die Liste tatsächlich ein Satz war, können Sie eine benutzerdefinierte Codierung verwenden. So etwas
return {'type':'set', 'list':list(obj)}
könnte funktionieren.Um verschachtelte Typen zu veranschaulichen, sollten Sie Folgendes serialisieren:
Dies löst den folgenden Fehler aus:
Dies zeigt an, dass der Encoder das zurückgegebene
list
Ergebnis übernimmt und den Serializer für seine untergeordneten Elemente rekursiv aufruft. So fügen Sie einen benutzerdefinierten Serializer für mehrere Typen hinzu:quelle
default
Funktion erneut aufgerufen, diesmalobj
als Datumsobjekt. Sie müssen es nur testen und eine Datumsdarstellung zurückgeben.Ich habe Raymond Hettingers Lösung an Python 3 angepasst .
Folgendes hat sich geändert:
unicode
verschwundendefault
mitsuper()
base64
um denbytes
Typ in zu serialisierenstr
(da es scheint, dassbytes
in Python 3 nicht in JSON konvertiert werden kann)quelle
json.dumps()
kehrt in / aus'latin1'
, das Überspringen vonbase64
Sachen , die nicht notwendig ist.In JSON sind nur Wörterbücher, Listen und primitive Objekttypen (int, string, bool) verfügbar.
quelle
Sie müssen keine benutzerdefinierte Encoder-Klasse
default
erstellen , um die Methode bereitzustellen. Sie kann als Schlüsselwortargument übergeben werden:führt zu
[1, 2, 3]
allen unterstützten Python-Versionen.quelle
Wenn Sie nur Mengen und keine allgemeinen Python-Objekte codieren müssen und diese leicht lesbar halten möchten, können Sie eine vereinfachte Version der Antwort von Raymond Hettinger verwenden:
quelle
Wenn Sie nur einen schnellen Speicherauszug benötigen und keinen benutzerdefinierten Encoder implementieren möchten. Sie können Folgendes verwenden:
json_string = json.dumps(data, iterable_as_array=True)
Dadurch werden alle Mengen (und andere iterable) in Arrays konvertiert. Achten Sie nur darauf, dass diese Felder Arrays bleiben, wenn Sie den JSON zurück analysieren. Wenn Sie die Typen beibehalten möchten, müssen Sie einen benutzerdefinierten Encoder schreiben.
quelle
Ein Nachteil der akzeptierten Lösung besteht darin, dass ihre Ausgabe sehr pythonspezifisch ist. Das heißt, seine rohe JSON-Ausgabe kann von einem Menschen nicht beobachtet oder von einer anderen Sprache (z. B. Javascript) geladen werden. Beispiel:
Kriege dich:
Ich kann eine Lösung vorschlagen, die das Set auf ein Diktat herabstuft, das auf dem Weg nach draußen eine Liste enthält, und zurück zu einem Set, wenn es mit demselben Encoder in Python geladen wird, wodurch Beobachtbarkeit und Sprachunabhängigkeit erhalten bleiben:
Was bringt dich:
Beachten Sie, dass das Serialisieren eines Wörterbuchs, das ein Element mit einem Schlüssel enthält
"__set__"
, diesen Mechanismus unterbricht. So__set__
ist jetzt ein reservierterdict
Schlüssel geworden. Sie können natürlich auch einen anderen, tiefer verschleierten Schlüssel verwenden.quelle