将枚举成员序列化为JSON
我想知道怎么把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 个回答
你只需要从 str
或 int
这个类继承就可以了:
from enum import Enum, unique
@unique
class StatusEnum(int, Enum):
pending: int = 11
approved: int = 15
declined: int = 266
就这样,使用任何 JSON 编码器都能进行序列化。
在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)
正确的答案取决于你打算如何使用序列化后的版本。
如果你打算把它反序列化回Python,可以参考Zero的回答。
如果你的序列化版本要用到其他语言,那么你可能想用IntEnum
,它会自动序列化成对应的整数:
from enum import IntEnum
import json
class Status(IntEnum):
success = 0
failure = 1
json.dumps(Status.success)
这样返回的是:
'0'
我知道这个问题有点老了,但我觉得这对大家会有帮助。我刚刚遇到过这个问题,发现如果你使用字符串枚举,把你的枚举声明为 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,这个方法是个不错的选择。
如果你想把一个任意的 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'}