Jeśli chcesz zakodować dowolny enum.Enum
element członkowski do formatu JSON, a następnie zdekodować go jako ten sam element członkowski wyliczenia (zamiast po prostu value
atrybut elementu członkowskiego wyliczenia ), możesz to zrobić, pisząc JSONEncoder
klasę niestandardową i funkcję dekodującą, która ma zostać przekazana jako object_hook
argument do json.load()
lub json.loads()
:
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
as_enum
Funkcja polega na JSON został zakodowany z użyciem EnumEncoder
, albo coś, co zachowuje się identycznie do niego.
Ograniczenie do członków PUBLIC_ENUMS
jest konieczne, aby uniknąć wykorzystania złośliwie spreparowanego tekstu w celu, na przykład, oszukania wywołującego kodu do zapisania prywatnych informacji (np. Tajnego klucza używanego przez aplikację) w niepowiązanym polu bazy danych, skąd mógłby zostać ujawniony (patrz http://chat.stackoverflow.com/transcript/message/35999686#35999686 ).
Przykładowe użycie:
>>> 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'}
if isinstance(obj, Enum):
?Wiem, że to stare, ale czuję, że to pomoże ludziom. Właśnie przejrzałem ten dokładny problem i odkryłem, że jeśli używasz wyliczeń łańcuchowych, deklarowanie wyliczeń jako podklasy
str
działa dobrze w prawie wszystkich sytuacjach:import json from enum import Enum class LogLevel(str, Enum): DEBUG = 'DEBUG' INFO = 'INFO' print(LogLevel.DEBUG) print(json.dumps(LogLevel.DEBUG)) print(json.loads('"DEBUG"')) print(LogLevel('DEBUG'))
Wyświetli:
LogLevel.DEBUG "DEBUG" DEBUG LogLevel.DEBUG
Jak widać, ładowanie JSON generuje ciąg,
DEBUG
ale można go łatwo przesłać z powrotem do obiektu LogLevel. Dobra opcja, jeśli nie chcesz tworzyć niestandardowego JSONEncodera.źródło
class LogLevel(str, Enum): DEBUG = 'Дебаг' INFO = 'Инфо'
w tym przypadkuenum with str
nie działa poprawnie (Prawidłowa odpowiedź zależy od tego, co zamierzasz zrobić z wersją serializowaną.
Jeśli zamierzasz cofnąć serializację z powrotem do Pythona, zobacz odpowiedź Zero .
Jeśli twoja wersja serializowana jest przenoszona do innego języka, prawdopodobnie chcesz użyć
IntEnum
zamiast niej, który jest automatycznie serializowany jako odpowiednia liczba całkowita:from enum import IntEnum import json class Status(IntEnum): success = 0 failure = 1 json.dumps(Status.success)
a to zwraca:
'0'
źródło
Python3.4
, a ta odpowiedź dotyczy wersji 3.4+.EnumMeta
zamiastIntEnum
Enum
, prawdopodobnie zstr
miksem -class MyStrEnum(str, Enum): ...
EnumMeta
, które było przeznaczone tylko jako metaklasa. Zamiast tego zauważ, że implementacjaIntEnum
jest jednolinijkowa i możesz osiągnąć to samo zastr
pomocąclass StrEnum(str, Enum): ...
.W Pythonie 3.7 można po prostu użyć
json.dumps(enum_obj, default=str)
źródło
name
enum w łańcuchu json. Lepszym sposobem będzie użycievalue
wyliczenia.json.dumps(enum_obj, default=lambda x: x.value)
Podobała mi się odpowiedź Zero Piraeus, ale zmodyfikowałem ją nieco do pracy z API dla Amazon Web Services (AWS) znanym jako Boto.
class EnumEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Enum): return obj.name return json.JSONEncoder.default(self, obj)
Następnie dodałem tę metodę do mojego modelu danych:
def ToJson(self) -> str: return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)
Mam nadzieję, że to komuś pomoże.
źródło
ToJson
do swojego modelu danych?Jeśli używasz
jsonpickle
najłatwiejszego sposobu, powinieneś wyglądać jak poniżej.from enum import Enum import jsonpickle @jsonpickle.handlers.register(Enum, base=True) class EnumHandler(jsonpickle.handlers.BaseHandler): def flatten(self, obj, data): return obj.value # Convert to json friendly format if __name__ == '__main__': class Status(Enum): success = 0 error = 1 class SimpleClass: pass simple_class = SimpleClass() simple_class.status = Status.success json = jsonpickle.encode(simple_class, unpicklable=False) print(json)
Po serializacji Json będziesz mieć zgodnie z oczekiwaniami
{"status": 0}
zamiast{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}
źródło
To zadziałało dla mnie:
class Status(Enum): success = 0 def __json__(self): return self.value
Nie musiałem niczego zmieniać. Oczywiście uzyskasz tylko wartość z tego i będziesz musiał wykonać inną pracę, jeśli chcesz później przekonwertować zserializowaną wartość z powrotem na wyliczenie.
źródło
JSONEncoder
gdzieś niestandardową ?