Python 将枚举成员序列化为 JSON
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24481852/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Serialising an Enum member to JSON
提问by Bilal Syed Hussain
How do I serialise a Python Enum
member to JSON, so that I can deserialise the resulting JSON back into a Python object?
如何将 PythonEnum
成员序列化为 JSON,以便可以将生成的 JSON 反序列化回 Python 对象?
For example, this code:
例如,这段代码:
from enum import Enum
import json
class Status(Enum):
success = 0
json.dumps(Status.success)
results in the error:
导致错误:
TypeError: <Status.success: 0> is not JSON serializable
How can I avoid that?
我怎样才能避免这种情况?
采纳答案by Zero Piraeus
If you want to encode an arbitrary enum.Enum
member to JSON and then decode
it as the same enum member (rather than simply the enum member's value
attribute), you can do so by writing a custom JSONEncoder
class, and a decoding function to pass as the object_hook
argument to json.load()
or json.loads()
:
如果要将任意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
The as_enum
function relies on the JSON having been encoded using EnumEncoder
, or something which behaves identically to it.
该as_enum
函数依赖于使用 编码的 JSON EnumEncoder
,或者与它行为相同的东西。
The restriction to members of PUBLIC_ENUMS
is necessary to avoid a maliciously crafted text being used to, for example, trick calling code into saving private information (e.g. a secret key used by the application) to an unrelated database field, from where it could then be exposed (see http://chat.stackoverflow.com/transcript/message/35999686#35999686).
对成员的限制PUBLIC_ENUMS
是必要的,以避免恶意制作的文本被用来,例如,欺骗调用代码将私人信息(例如应用程序使用的密钥)保存到不相关的数据库字段,然后从那里暴露出来(参见http://chat.stackoverflow.com/transcript/message/35999686#35999686)。
Example usage:
用法示例:
>>> 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'}
回答by Ethan Furman
The correct answer depends on what you intend to do with the serialized version.
正确答案取决于您打算对序列化版本做什么。
If you are going to unserialize back into Python, see Zero's answer.
如果您要反序列化回 Python,请参阅Zero's answer。
If your serialized version is going to another language then you probably want to use an IntEnum
instead, which is automatically serialized as the corresponding integer:
如果您的序列化版本将使用另一种语言,那么您可能想要使用 anIntEnum
代替,它会自动序列化为相应的整数:
from enum import IntEnum
import json
class Status(IntEnum):
success = 0
failure = 1
json.dumps(Status.success)
and this returns:
这将返回:
'0'
回答by Pretzel
I liked Zero Piraeus' answer, but modified it slightly for working with the API for Amazon Web Services (AWS) known as Boto.
我喜欢 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)
I then added this method to my data model:
然后我将此方法添加到我的数据模型中:
def ToJson(self) -> str:
return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)
I hope this helps someone.
我希望这可以帮助别人。
回答by Justin Carter
I know this is old but I feel this will help people. I just went through this exact problem and discovered if you're using string enums, declaring your enums as a subclass of str
works well for almost all situations:
我知道这很旧,但我觉得这会帮助人们。我刚刚经历了这个确切的问题,并发现如果您使用字符串枚举,将您的枚举声明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'))
Will output:
将输出:
LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG
As you can see, loading the JSON outputs the string DEBUG
but it is easily castable back into a LogLevel object. A good option if you don't want to create a custom JSONEncoder.
如您所见,加载 JSON 会输出字符串,DEBUG
但它很容易转换回 LogLevel 对象。如果您不想创建自定义 JSONEncoder,这是一个不错的选择。
回答by rafalkasa
If you are using jsonpickle
the easiest way should look as below.
如果您使用的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)
After Json serialization you will have as expected {"status": 0}
instead of
在 Json 序列化之后,您将按预期{"status": 0}
而不是
{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}
回答by kai
In Python 3.7, can just use
json.dumps(enum_obj, default=str)
在 Python 3.7 中,可以只使用
json.dumps(enum_obj, default=str)