So ändern Sie json-Kodierung Verhalten für python-Objekt serialisierbar?
Ist es einfach, ändern Sie das format von einem Objekt, das nicht als JSON serialisierbar z.B. datetime.datetime.
Meine Forderung, für debugging-Zwecke ist die Art und Weise ändern einige benutzerdefinierte Objekte erweitert, von der Basis wie dict
und list
, serialisiert im json-format . Code :
import datetime
import json
def json_debug_handler(obj):
print("object received:")
print type(obj)
print("\n\n")
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return None
class mDict(dict):
pass
class mList(list):
pass
def test_debug_json():
games = mList(['mario','contra','tetris'])
games.src = 'console'
scores = mDict({'dp':10,'pk':45})
scores.processed = "unprocessed"
test_json = { 'games' : games , 'scores' : scores , 'date': datetime.datetime.now() }
print(json.dumps(test_json,default=json_debug_handler))
if __name__ == '__main__':
test_debug_json()
DEMO : http://ideone.com/hQJnLy
Ausgabe:
{"date": "2013-05-07T01:03:13.098727", "games": ["mario", "contra", "tetris"], "scores": {"pk": 45, "dp": 10}}
Gewünschte Ausgabe:
{"date": "2013-05-07T01:03:13.098727", "games": { "orig": ["mario", "contra", "tetris"] ,"attrs" : { "src":"console"}} , "scores": { "orig": {"pk": 45, "dp": 10},"attrs":
"processed":"unprocessed }}
Macht die default
- handler nicht für serialisierbare Objekte ?
Wenn nicht, wie kann ich das überschreiben, ohne das hinzufügen von toJSON Methoden, um die erweiterten Klassen ?
Außerdem gibt es diese version der JSON-encoder, der nicht funktioniert :
class JsonDebugEncoder(json.JSONEncoder):
def default(self,obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat()
elif isinstance(obj,mDict):
return {'orig':obj , 'attrs': vars(obj)}
elif isinstance(obj,mList):
return {'orig':obj, 'attrs': vars(obj)}
else:
return json.JSONEncoder.default(self, obj)
Wenn es einen hack mit pickle,__getstate__,__setstate__,
und dann mit json.dumps über Gurke.lädt Objekt , ich bin offen, wie gut, habe ich versucht , aber das hat nicht funktioniert.
- Mit einer geeigneten Klasse mit einem
__getstate()__
- Methode sollte funktionieren. Mehr: stackoverflow.com/q/12627949/139010 - Für Unterklassen der
dict
undjson
finden Sie unter Überschreiben von verschachtelten JSON-Kodierung von geerbten Standard-unterstützt Objekte wie dict, list - Das ist äquivalent zum schreiben eines komplexen benutzerdefinierten encoder,ich hoffe, es sollte ein einfacher Weg, um wieder eine andere represntation der Objekt-Encoder wie json oder Gurke ?
- Der default-handler nur aufgerufen wird, wird für Objekte, die es nicht serialisieren. Da Sie Erben von Objekten kann es zu serialisieren, wird es führen Sie es mit der Standard-Serialisierung. Aber ich hatte etwas ähnliches vor ein paar Tagen. Hier ist " mein Ansatz: stackoverflow.com/questions/16361223/...
pickle
unterstützt die Zustand Haken (__getstate__
und Gefährten), aberjson
unterstützt nicht solche Methoden hilfreich.- Es klingt wie Sie wollen Bearbeiten Sie Ihre Daten wie in einem javascript-Objekt, bevor Sie es zu serialisieren. Vielleicht so etwas wie jsobject nützlich sein könnte für Sie. Das sieht aus wie es hat einen json-encoder und-decoder gebündelt.
- Würden Sie die Lösungen mit 3rd-party-json-Bibliotheken, oder sind Sie gebunden an die stlib
json
Modul? - eine gut gepflegte 3rd-party-json-Bibliothek tun würde.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Scheint es, dass zu erreichen, das gewünschte Verhalten mit den gegebenen Einschränkungen, müssen Sie Tauchen Sie ein in die
JSONEncoder
Klasse ein wenig. Unten habe ich geschrieben aus einer benutzerdefiniertenJSONEncoder
, der überschreibt dieiterencode
Methode zum übergeben eines benutzerdefiniertenisinstance
Methode_make_iterencode
. Es ist nicht die sauberste Sache der Welt, aber es scheint der beste zu sein angesichts der Möglichkeiten, und es hält die Anpassung auf ein minimum.Können Sie jetzt eine Unterklasse der
CustomObjectEncoder
so ist es korrekt serialisiert benutzerdefinierte Objekte. DieCustomObjectEncoder
können auch Coole Sachen wie Griff verschachtelte Objekte.Die Antwort von FastTurtle könnte eine viel sauberere Lösung.
Hier ist etwas in der Nähe, was Sie wollen, basierend auf der Technik wie bereits in meiner Frage/Antwort: Überschreiben verschachtelte JSON-Kodierung von geerbten Standard-unterstützt Objekte wie dict, list
Diese Ergebnisse, in:
Diese Weise können Sie Sie verschlüsseln und zu entschlüsseln es zurück auf die python-Objekt kam.
EDIT:
Hier eine version, die tatsächlich kodiert es um die Ausgabe, die Sie wollte, und kann Sie entschlüsseln, wie gut. Wenn ein Wörterbuch enthält 'orig' und 'attr' es wird überprüft, ob 'orig' enthält ein Wörterbuch oder eine Liste, wenn ja wird es bzw konvertieren Sie das Objekt zurück, das mDict oder mList.
Hier einige weitere Infos zu der Ausgabe:
Sorry für eventuelle schlechte Namenskonventionen, es ist ein quick-setup. 😉
Hinweis: Die datetime-nicht decodiert zurück auf die python-Repräsentation. Die Umsetzung getan werden könnte, indem für jeden dict-key namens "Datum" und enthält eine gültige Zeichenfolgendarstellung eines datetime.
Als andere haben darauf hingewiesen, bereits der default-handler nur aufgerufen wird, für Werte, die nicht einer der anerkannten Arten. Meine vorgeschlagene Lösung für dieses problem ist Vorverarbeiten das Objekt, das Sie serialisieren möchten, recursing über Listen, Tupel und dictionaries, aber Verpackung jeder andere Wert in einer benutzerdefinierten Klasse.
Etwas wie dieses:
Würden Sie diese Funktion aufrufen, bevor Sie Ihr Objekt json.dumps, wie diese:
Beachten Sie, dass dieser code wird überprüft, für Objekte, deren Klasse genau entspricht, eine Liste, ein Tupel oder ein Wörterbuch, um benutzerdefinierte Objekte erweitert, von diesen Arten eingeschlossen werden, anstatt analysiert. Als Ergebnis der regelmäßigen Listen, Tupeln und dictionaries werden serialisiert werden, wie üblich, aber alle anderen Werte werden an den default-handler.
Das Ergebnis von all diesem ist, dass jeder Wert, der erreicht das der default-handler garantiert werden, eingewickelt in eine dieser Debug-Klassen. So die erste Sache, die Sie gehen zu wollen, zu tun ist, entpacken Sie das ursprüngliche Objekt, so wie hier:
Können Sie dann prüfen Sie das original-Objekt mit dem Typ und handle welche Arten brauchen eine spezielle Verarbeitung. Für alles andere, sollte man einfach nur wieder die original-Objekt (also der letzten Rückkehr aus dem handler sollte
return obj
nichtreturn None
).Beachten Sie, dass dieser code überprüft nicht, für Werte, die nicht serialisierbar sind. Diese fallen durch die Letzte
return obj
, dann wird abgelehnt, durch die serializer und ging zurück zu der default-handler wieder - nur dieses mal ohne Debug-wrapper.Wenn Sie brauchen, um deal mit, das Szenario, man könnte hinzufügen, ein check an der Spitze des handlers so:
Ideone demo: http://ideone.com/tOloNq
Die default-Funktion wird nur aufgerufen, wenn der Knoten gedumpt ist nicht nativ serializable, und Ihre mDict-Klassen zu serialisieren ist. Hier eine kleine demo, die zeigt, dass, wenn Sie standardmäßig aufgerufen wird und Wann nicht:
Und die Ausgabe:
Beachten Sie, dass die sets sind nicht nativ serializable, aber dicts sind.
Da Ihre m___ - Klassen serialisierbar sind, Ihrer Prozedur nie aufgerufen wird.
Update #1 -----
Könnte man ändern JSON-encoder-code. Die details, wie dies zu tun, hängt davon ab, welche JSON-Implementierung, die Sie verwenden. Zum Beispiel in simplejson, der entsprechende code ist dieser, in encode.py:
In anderen Worten, es ist ein hard-wired Verhalten, dass Anrufe standardmäßig nur, wenn der Knoten codiert ist nicht einer der anerkannten Basis-Typen. Man könnte diese Einstellungen in eine von mehreren Möglichkeiten:
1 -- Unterklasse JSONEncoder, wie Sie getan haben, die oben, aber fügen Sie einen parameter auf den Initialisierer angibt, die die Funktion verwendet anstelle des standard-_make_iterencode, in dem Sie fügen Sie einen test, dass Sie nennen würde-Standard-Klassen, die Ihren Suchkriterien entsprechen. Dies ist eine saubere Herangehensweise, da Sie sich nicht ändern das JSON-Modul, aber Sie würden sich erneut auf eine Menge von code aus dem original _make_iterencode. (Andere Variationen dieses Ansatzes gehören monkeypatching _make_iterencode oder dessen sub-Funktion _iterencode_dict).
2 -- ändern Sie die JSON-Modul-Quelle, und verwenden Sie die
__debug__
ständige um das Verhalten zu ändern:Ideal der JSONEncoder Klasse würde einen parameter angeben "verwenden Sie Standard für alle Typen", aber es funktioniert nicht. Das oben genannte ist eine einfache ein-Zeit zu ändern, die tut, was Sie suchen.
Versuchen Sie, die unten. Es erzeugt die Ausgabe, die Sie wollen, und sieht relativ einfach. Der einzige wirkliche Unterschied zu einem encoder-Klasse ist, dass wir überschreiben sollten beide decode und encode-Methoden (denn die letztere ist immer noch als für Arten der encoder weiß, wie zu handhaben).
Warum nicht erstellen Sie einfach einen neuen Objekttyp zu passieren, um den encoder? Versuchen:
Könnte man hinzufügen, die Validierung auf der mDict und mList, falls gewünscht.
Wenn Sie definieren diese überschreiben die
__instancecheck__
:dann patch
json.encoder
wie diese überschreiben die_make_iterencode.func_defaults
:... dein Beispiel würde fast wörtlich:
Die Dinge, die ich ändern musste waren um sicherzustellen, dass es keine Zyklen:
und fügen Sie diese irgendwo vor
test_debug_json
:hier ist meine Ausgabe in der Konsole:
Wenn Sie sind in der Lage, die Weise zu ändern
json.dumps
genannt wird. Sie können alles tun, die Verarbeitung erforderlich ist, bevor der JSON-encoder bekommt seine Hände auf Sie. Diese version nicht verwenden, jede Art von kopieren und Bearbeiten Sie die Strukturen im Ort. Sie können hinzufügencopy()
wenn erforderlich.Rufen Sie
json_debug_handler
auf das Objekt, das Sie serialisieren, vor der übergabe an diejson.dumps
. Mit diesem Muster könnte man auch einfach die änderungen rückgängig machen und/oder zusätzliche Umwandlung Regeln.edit:
Wenn Sie nicht ändern können, wie
json.dumps
heißt, Sie können immer monkeypatch es zu tun, was Sie wollen. Wie dies zu tun:Können wir nur Vorverarbeiten der
test_json
machen es geeignet für Ihre Anforderung? Es ist leichter zu manipulieren, ein python dict schreiben als eine nutzlose Enkodieren.Sollten Sie in der Lage zu überschreiben JSONEncoder.encode():
und dann, wenn Sie wollen, um den patch in
json.dumps
sieht es aus http://docs.buildbot.net/latest/reference/json-pysrc.html wie Sie benötigen, zu ersetzenjson._default_encoder
mit einer Instanz vonMyEncoder
.encode
nicht genannt für verschachtelte Werte.Wenn Sie nur auf der Suche für die Serialisierung und nicht Deserialisierung dann können Sie das Objekt vor dem senden an
json.dumps
. Siehe unten BeispielDen Ausgang der gleiche ist
Ist es auch möglich, das überschreiben der JSONEncoder, aber da es verwendet verschachtelte Methoden, es wäre Komplex und erfordern Techniken, die in unten
Können Sie patch *nur* eine verschachtelte Funktion mit Verschluss, oder muss die ganze äußere Funktion wiederholt werden?
Da Sie wollen, Dinge einfach zu halten, würde ich nicht empfehlen zu gehen, dass die route
Entlang der Linien von FastTurtle Vorschlag, aber erfordert etwas weniger code und viel tiefer monkeying, können Sie überschreiben
isinstance
sich, weltweit. Dies ist wahrscheinlich Nicht Eine Gute Idee, und wird Sie vielleicht etwas zu brechen. Aber es funktioniert, er produziert Sie Ihre gewünschte Ausgabeformat, und es ist ganz einfach.Zunächst bevor json importiert wird überall, monkey-patch der gelieferten Modul zu ersetzen
isinstance
mit einem, liegt, nur ein wenig, und nur in einem bestimmten Kontext:Erstellen Sie dann Ihre benutzerdefinierten encoder, der Umsetzung Ihrer benutzerdefinierten Serialisierung und zwingt die Verwendung von
_make_iterencode
(seit der c-version sind nicht betroffen durch die monkeypatching):- Und das ist wirklich alles dort ist zu ihm! Die Ausgabe von Python 3 und Python 2 unten.
Ich versuche, ändern Sie die Standard-resolver-Priorität, und ändern Sie den Standard-iterator-Ausgänge zur Erreichung Ihrer Zwecke.
ändern Sie die Standard-resolver-Priorität, ausgeführt vor allen standard-Typ überprüfen:
ändern Sie den Standard-iterator output;