将枚举成员序列化为JSON

211 投票
9 回答
121626 浏览
提问于 2025-04-18 11:31

我想知道怎么把Python中的Enum成员转换成JSON格式,这样我就可以把生成的JSON再转回Python对象。

比如,下面这段代码:

from enum import Enum    
import json

class Status(Enum):
    success = 0

json.dumps(Status.success)

会出现这个错误:

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

我该怎么做才能避免这个错误呢?

9 个回答

35

你只需要从 strint 这个类继承就可以了:

from enum import Enum, unique

@unique            
class StatusEnum(int, Enum):
    pending: int = 11                                      
    approved: int = 15                                       
    declined: int = 266

就这样,使用任何 JSON 编码器都能进行序列化。

41

在Python 3.7及以上版本中,你可以直接使用

json.dumps(enum_obj, default=str)

如果你想用枚举的值,可以这样做:

json.dumps(enum_obj, default=lambda x: x.value)

或者如果你想用枚举的名字,可以这样:

json.dumps(enum_obj, default=lambda x: x.name)

117

正确的答案取决于你打算如何使用序列化后的版本。

如果你打算把它反序列化回Python,可以参考Zero的回答

如果你的序列化版本要用到其他语言,那么你可能想用IntEnum,它会自动序列化成对应的整数:

from enum import IntEnum
import json

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

json.dumps(Status.success)

这样返回的是:

'0'
302

我知道这个问题有点老了,但我觉得这对大家会有帮助。我刚刚遇到过这个问题,发现如果你使用字符串枚举,把你的枚举声明为 str 的子类在几乎所有情况下都能很好地工作:

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'))

输出结果将是:

LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG

如你所见,加载 JSON 时输出的是字符串 DEBUG,但它很容易转换回 LogLevel 对象。如果你不想创建一个自定义的 JSONEncoder,这个方法是个不错的选择。

75

如果你想把一个任意的 enum.Enum 成员编码成 JSON 格式,然后再把它解码回同样的枚举成员(而不是仅仅获取这个枚举成员的 value 属性),你可以通过编写一个自定义的 JSONEncoder 类,以及一个解码函数来实现。这个解码函数需要作为 object_hook 参数传递给 json.load()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 函数依赖于 JSON 是通过 EnumEncoder 编码的,或者是与它行为相同的其他方式。

限制只能使用 PUBLIC_ENUMS 中的成员是为了避免恶意构造的文本被用来,比如欺骗调用代码将私密信息(例如应用程序使用的秘密密钥)保存到不相关的数据库字段中,这样就可能会被泄露(参见 https://chat.stackoverflow.com/transcript/message/35999686#35999686)。

示例用法:

>>> 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'}

撰写回答