将Enum成员序列化为JSON

时间:2014-06-30 01:24:12

标签: python json python-3.x serialization enums

如何将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

我该如何避免?

8 个答案:

答案 0 :(得分:45)

正确答案取决于您打算如何处理序列化版本。

如果要重新反序列化为Python,请参阅Zero's answer

如果您的序列化版本使用其他语言,那么您可能希望使用IntEnum代替,它会自动序列化为相应的整数:

from enum import IntEnum
import json

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

json.dumps(Status.success)

然后返回:

'0'

答案 1 :(得分:37)

如果要将任意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函数依赖于使用EnumEncoder编码的JSON,或者与其行为完全相同的东西。

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

答案 2 :(得分:33)

我知道这很老了,但我认为这会帮助人们。我刚刚经历了这个确切的问题,发现您是否使用字符串枚举,将您的枚举声明为unwind的子类几乎可以在所有情况下使用:

{
    "$match": match
},
{
    "$lookup": {
        from: "lists",
        localField: "list",
        foreignField: "_id",
        as: "list"
    }
},
{
    "$unwind": "$list"
},

将输出:

str

如您所见,加载JSON会输出字符串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对象。如果您不想创建自定义JSONEncoder,则是一个不错的选择。

答案 3 :(得分:8)

我喜欢Zero Piraeus&#39;回答,但对于使用称为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)

然后我将此方法添加到我的数据模型中:

    def ToJson(self) -> str:
        return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)

我希望这有助于某人。

答案 4 :(得分:2)

您只需要从 strint 类继承:

from enum import Enum, unique

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

就是这样,它将使用任何 JSON 编码器进行序列化。

答案 5 :(得分:0)

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

Json序列化后,您将得到预期的{"status": 0}而不是

{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}

答案 6 :(得分:0)

在Python 3.7中,只能使用 json.dumps(enum_obj, default=str)

答案 7 :(得分:-2)

这对我有用:

class Status(Enum):
    success = 0

    def __json__(self):
        return self.value

无需更改其他任何内容。显然,您将仅从中获得值,并且如果以后想要将序列化的值转换回枚举,则需要做一些其他工作。