2014-06-30 8 views
32

Wie kann ich ein Python-Member Enum in JSON serialisieren, damit ich das resultierende JSON-Objekt in ein Python-Objekt zurückspeichern kann?Serialisierung eines Enum-Members in JSON

Zum Beispiel dieses Code:

from enum import Enum  
import json 

class Status(Enum): 
    success = 0 

json.dumps(Status.success) 

Ergebnisse in dem Fehler:

TypeError: <Status.success: 0> is not JSON serializable 

Wie kann ich das vermeiden?

+1

@ZeroPiraeus: Gerade darüber gestolpert. Ich hatte keine Ahnung, dass ich so viele geantwortet hatte! :) –

Antwort

24

Wenn Sie ein beliebiges Element enum.Enum in JSON codieren und dann es als dasselbe Enum-Element (lieber als das Enum-Member value Attribut) dekodieren möchten, können Sie das tun so durch eine benutzerdefinierte JSONEncoder Klasse zu schreiben, und eine Decodierfunktion als object_hook Argument json.load() oder json.loads() passieren:

PUBLIC_ENUMS = { 
    'Status': Status, 
    # ... 
} 

class EnumEncoder(json.JSONEncoder): 
    def default(self, obj): 
     if type(obj) in PUBLIC_ENUMS.values(): 
      return {"__enum__": str(obj)} 
     return json.JSONEncoder.default(self, obj) 

def as_enum(d): 
    if "__enum__" in d: 
     name, member = d["__enum__"].split(".") 
     return getattr(PUBLIC_ENUMS[name], member) 
    else: 
     return d 

Die as_enum Funktion auf der JSON beruht codiert worden EnumEncoder, oder etwas, das sie identisch zu verhält . Die Beschränkung auf Mitglieder von PUBLIC_ENUMS ist notwendig, um zu verhindern, dass ein in böser Absicht erstellter Text zum Beispiel den Aufruf von Code zum Speichern privater Informationen (z. B. eines von der Anwendung verwendeten geheimen Schlüssels) in ein nicht verwandtes Datenbankfeld verwendet es könnte dann belichtet werden (siehe http://chat.stackoverflow.com/transcript/message/35999686#35999686).

Beispiel Nutzung:

>>> data = { 
...  "action": "frobnicate", 
...  "status": Status.success 
... } 
>>> text = json.dumps(data, cls=EnumEncoder) 
>>> text 
'{"status": {"__enum__": "Status.success"}, "action": "frobnicate"}' 
>>> json.loads(text, object_hook=as_enum) 
{'status': <Status.success: 0>, 'action': 'frobnicate'} 
+0

funktioniert sehr gut, danke –

+1

Danke, Null! Nettes Beispiel. –

+0

Wenn Sie Ihren Code in einem Modul haben (zum Beispiel enumencoder.py), müssen Sie die Klasse, die Sie analysieren, von JSON in dict importieren. In diesem Fall müssen Sie beispielsweise die Klasse * Status * im Modul enumencoder.py importieren. –

20

Die richtige Antwort hängt davon ab, was Sie mit der serialisierten Version machen wollen.

Wenn Sie wieder in Python denialisieren möchten, finden Sie weitere Informationen unter Zero's answer.

Wenn Ihre serialisierte Version in einer anderen Sprache geht dann wollen Sie wahrscheinlich ein IntEnum stattdessen verwenden, die wie die entsprechende ganze Zahl automatisch serialisiert:

from enum import IntEnum 
import json 

class Status(IntEnum): 
    success = 0 
    failure = 1 

json.dumps(Status.success) 

und diese zurück:

'0' 
+0

Ich möchte in Python wieder unserialisieren –

+0

In python3.2, Dies ist nicht tatsächlich zurück, was die Antwort sagt. Ich bekomme: '>>> json.dumps (Status.success)' -> ''Status.success'' – AShelly

+3

@AShelly: Die Frage wurde mit' Python3.4' markiert, und diese Antwort ist 3.4+ spezifisch. –

5

mochte ich Null Piräus Antwort, aber es leicht modifiziert mit der API für Amazon Web Services (AWS), bekannt als Boto für die Arbeit.

class EnumEncoder(json.JSONEncoder): 
def default(self, obj): 
    if isinstance(obj, Enum): 
     return obj.name 
    return json.JSONEncoder.default(self, obj) 

Ich habe dann diese Methode, um meine Datenmodell:

def ToJson(self) -> str: 
     return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True) 

Ich hoffe, das jemand hilft.