Bil*_*ain 66 python enums serialization json python-3.x
如何将Python Enum成员序列化为JSON,以便将生成的JSON反序列化为Python对象?
例如,这段代码:
from enum import Enum
import json
class Status(Enum):
success = 0
json.dumps(Status.success)
Run Code Online (Sandbox Code Playgroud)
导致错误:
TypeError: <Status.success: 0> is not JSON serializable
Run Code Online (Sandbox Code Playgroud)
我怎么能避免这种情况?
Jus*_*ter 57
我知道这已经过时了,但我觉得这会对人有所帮助.我刚刚解决了这个确切的问题并发现如果你正在使用字符串枚举,将你的枚举声明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'))
Run Code Online (Sandbox Code Playgroud)
将输出:
LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG
Run Code Online (Sandbox Code Playgroud)
如您所见,加载JSON会输出字符串,DEBUG但很容易将其转换回LogLevel对象.如果您不想创建自定义JSONEncoder,这是一个不错的选择.
Eth*_*man 52
正确答案取决于您打算如何处理序列化版本.
如果要将其反序列化为Python,请参阅Zero的答案.
如果你的序列化版本是另一种语言,那么你可能想要使用一个IntEnum,它会被自动序列化为相应的整数:
from enum import IntEnum
import json
class Status(IntEnum):
success = 0
failure = 1
json.dumps(Status.success)
Run Code Online (Sandbox Code Playgroud)
这会返回:
'0'
Run Code Online (Sandbox Code Playgroud)
Zer*_*eus 43
如果要将任意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
Run Code Online (Sandbox Code Playgroud)
该as_enum函数依赖于已使用编码的JSON EnumEncoder,或者与其行为相同的东西.
对成员的限制PUBLIC_ENUMS是必要的,以避免恶意制作的文本被用于,例如,欺骗调用代码将私人信息(例如应用程序使用的密钥)保存到不相关的数据库字段,然后从那里暴露出来(见http://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'}
Run Code Online (Sandbox Code Playgroud)
Far*_*eed 35
您只需要继承str或int类:
from enum import Enum, unique
@unique
class StatusEnum(int, Enum):
pending: int = 11
approved: int = 15
declined: int = 266
Run Code Online (Sandbox Code Playgroud)
就是这样,它将使用任何 JSON 编码器进行序列化。
kai*_*kai 25
在 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)
我喜欢Zero Piraeus的回答,但是对于使用称为Boto的Amazon Web Services(AWS)的API进行了一些修改.
class EnumEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Enum):
return obj.name
return json.JSONEncoder.default(self, obj)
Run Code Online (Sandbox Code Playgroud)
然后我将此方法添加到我的数据模型中:
def ToJson(self) -> str:
return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)
Run Code Online (Sandbox Code Playgroud)
我希望这可以帮助别人.
如果您使用jsonpickle最简单的方法,应该如下所示。
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)
Run Code Online (Sandbox Code Playgroud)
Json 序列化后,您将得到预期的结果,{"status": 0}而不是
{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}
Run Code Online (Sandbox Code Playgroud)