将Enum成员序列化为JSON

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,这是一个不错的选择.

  • `class LogLevel(str, Enum): DEBUG = 'Дебаг' INFO = 'Инфо'` 在这种情况下 `enum with str` 无法正常工作( (4认同)
  • 谢谢。尽管我主要反对多重继承,但这非常简洁,这就是我所采用的方式。不需要额外的编码器:) (3认同)
  • 这个 str mixin 可能会产生意想不到的副作用:请参阅[这个问题](/sf/ask/4573774481/)。 (2认同)

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)

  • @AShelly:这个问题用`Python3.4`标记,这个答案是3.4+特定的. (5认同)
  • @bholagabbar:不,您将使用`Enum`,可能是`str` mixin-`class MyStrEnum(str,Enum):...` (3认同)
  • @bholagabbar,很有意思.您应该将解决方案作为答案发布. (3认同)
  • 完善.如果Enum是一个字符串,你将使用`EnumMeta`而不是`IntEnum` (2认同)
  • 我会避免直接从“EnumMeta”继承,它仅用作元类。相反,请注意 `IntEnum` 的实现[是一个单行代码](https://github.com/python/cpython/blob/3.7/Lib/enum.py#L670),并且您可以对 ` str` 和 `class StrEnum(str, Enum): ...`。 (2认同)

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

您只需要继承strint类:

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)

  • 枚举值可以通过 `json.dumps(enum_obj, default=lambda x: x.value)` 使用 (2认同)
  • 但是,如果使用枚举作为键,则这将不起作用。它仍然会抱怨“TypeError:键必须是 str、int、float、bool 或 None” - 上面的大多数解决方案都没有考虑作为键的用法。枚举应该是可散列的。 (2认同)

Pre*_*zel 9

我喜欢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)

我希望这可以帮助别人.


raf*_*asa 5

如果您使用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)