Serializar un miembro de Enum a JSON

Resuelto Bilal Syed Hussain asked hace 10 años • 9 respuestas

¿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?

Bilal Syed Hussain avatar Jun 30 '14 08:06 Bilal Syed Hussain
Aceptado

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 strfunciona 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.

Justin Carter avatar Aug 23 '2018 00:08 Justin Carter

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 IntEnumen 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'
Ethan Furman avatar Jun 30 '2014 02:06 Ethan Furman

Si desea codificar un enum.Enummiembro arbitrario en JSON y luego decodificarlo como el mismo miembro de enumeración (en lugar de simplemente el valueatributo del miembro de enumeración), puede hacerlo escribiendo una JSONEncoderclase personalizada y una función de decodificación para pasarla como object_hookargumento 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_enumfunción depende de que el JSON haya sido codificado usando EnumEncodero algo que se comporte de manera idéntica.

La restricción a los miembros de PUBLIC_ENUMSes 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'}
Zero Piraeus avatar Jun 30 '2014 04:06 Zero Piraeus