Se você deseja codificar um enum.Enum
membro arbitrário para JSON e, em seguida, decodificá-lo como o mesmo membro enum (em vez de simplesmente o value
atributo do membro enum ), você pode fazer isso escrevendo uma JSONEncoder
classe personalizada e uma função de decodificação para passar como o object_hook
argumento parajson.load()
ou 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
A as_enum
função depende do JSON ter sido codificado usando EnumEncoder
, ou algo que se comporta de forma idêntica a ele.
A restrição a membros de PUBLIC_ENUMS
é necessária para evitar que um texto elaborado com códigos maliciosos seja usado para, por exemplo, enganar o código de chamada para salvar informações privadas (por exemplo, uma chave secreta usada pelo aplicativo) em um campo de banco de dados não relacionado, de onde poderia ser exposto (consulte http://chat.stackoverflow.com/transcript/message/35999686#35999686 ).
Exemplo de uso:
>>> 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):
?Eu sei que isso é antigo, mas acho que isso vai ajudar as pessoas. Acabei de passar por esse problema exato e descobri se você está usando enums de string, declarar seus enums como uma subclasse de
str
funciona bem para quase todas as situações: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'))
Irá produzir:
LogLevel.DEBUG "DEBUG" DEBUG LogLevel.DEBUG
Como você pode ver, carregar o JSON gera a string,
DEBUG
mas é facilmente convertível de volta em um objeto LogLevel. Uma boa opção se você não deseja criar um JSONEncoder personalizado.fonte
class LogLevel(str, Enum): DEBUG = 'Дебаг' INFO = 'Инфо'
neste caso,enum with str
não funciona corretamente (A resposta correta depende do que você pretende fazer com a versão serializada.
Se você vai desserializar de volta para Python, veja a resposta de Zero .
Se sua versão serializada for para outro idioma, você provavelmente deseja usar um
IntEnum
, que é serializado automaticamente como o número inteiro correspondente:from enum import IntEnum import json class Status(IntEnum): success = 0 failure = 1 json.dumps(Status.success)
e isso retorna:
'0'
fonte
Python3.4
, e esta resposta é 3.4+ específica.EnumMeta
vez deIntEnum
Enum
, possivelmente com umstr
mixin -class MyStrEnum(str, Enum): ...
EnumMeta
, que era apenas uma metaclasse. Em vez disso, observe que a implementação deIntEnum
é de uma linha e você pode conseguir o mesmostr
comclass StrEnum(str, Enum): ...
.No Python 3.7, pode apenas usar
json.dumps(enum_obj, default=str)
fonte
name
of enum na string json. A melhor maneira será usarvalue
o enum.json.dumps(enum_obj, default=lambda x: x.value)
Gostei da resposta de Zero Piraeus, mas modifiquei um pouco para trabalhar com a API para Amazon Web Services (AWS) conhecida como Boto.
class EnumEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Enum): return obj.name return json.JSONEncoder.default(self, obj)
Em seguida, adicionei este método ao meu modelo de dados:
def ToJson(self) -> str: return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)
Espero que isso ajude alguém.
fonte
ToJson
ao seu modelo de dados?Se você estiver usando
jsonpickle
a maneira mais fácil, veja a seguir.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)
Após a serialização Json, você terá como esperado em
{"status": 0}
vez de{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}
fonte
Isso funcionou para mim:
class Status(Enum): success = 0 def __json__(self): return self.value
Não precisava mudar mais nada. Obviamente, você só obterá o valor disso e precisará fazer algum outro trabalho se quiser converter o valor serializado de volta para o enum posteriormente.
fonte
JSONEncoder
algum lugar?