Serializar un miembro de Enum a JSON
¿Cómo serializo un Python?Enum
miembro de Python en JSON para poder deserializar el JSON resultante nuevamente en un objeto de Python?
Por ejemplo, este código:
from enum import Enum
import json
class Status(Enum):
success = 0
json.dumps(Status.success)
da como resultado el error:
TypeError: <Status.success: 0> is not JSON serializable
¿Cómo puedo evitar eso?
Sé que esto es antiguo, pero creo que ayudará a la gente. Acabo de resolver este problema exacto y descubrí que si estás usando enumeraciones de cadenas, declararlas como una subclase str
funciona bien en casi todas las situaciones:
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'))
Salida:
LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG
Como puede ver, al cargar el JSON se genera la cadena DEBUG
, pero se puede volver a convertir fácilmente en un objeto LogLevel. Una buena opción si no desea crear un JSONEncoder personalizado.
La respuesta correcta depende de lo que pretenda hacer con la versión serializada.
Si va a deserializar nuevamente en Python, consulte la respuesta de Zero .
Si su versión serializada va a otro idioma, entonces probablemente quiera usar un IntEnum
en su lugar, que se serializa automáticamente como el número entero correspondiente:
from enum import IntEnum
import json
class Status(IntEnum):
success = 0
failure = 1
json.dumps(Status.success)
y esto devuelve:
'0'
Si desea codificar un enum.Enum
miembro arbitrario en JSON y luego decodificarlo como el mismo miembro de enumeración (en lugar de simplemente el value
atributo del miembro de enumeración), puede hacerlo escribiendo una JSONEncoder
clase personalizada y una función de decodificación para pasarla como object_hook
argumento json.load()
o 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
La as_enum
función depende de que el JSON haya sido codificado usando EnumEncoder
o algo que se comporte de manera idéntica.
La restricción a los miembros de PUBLIC_ENUMS
es necesaria para evitar que se utilice un texto elaborado con fines malintencionados, por ejemplo, para engañar al código de llamada para que guarde información privada (por ejemplo, una clave secreta utilizada por la aplicación) en un campo de base de datos no relacionado, desde donde podría quedar expuesto. (consulte https://chat.stackoverflow.com/transcript/message/35999686#35999686 ).
Uso de ejemplo:
>>> 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'}