【问题标题】:How do I serialize IntEnum from enum34 to json in python?如何在 python 中将 IntEnum 从 enum34 序列化为 json?
【发布时间】:2015-09-07 19:23:51
【问题描述】:

我正在使用 enum34 包,并已设法以自定义格式存储将 Enum 子类化为 json 的对象。但是,我有一个对象实例使用了相当多的子类 IntEnum,这似乎没有被我的自定义 JSONEncoder 正确拾取。

我最后的办法是将其转换为 Enum 并相应地重构所有相关代码。 IntEnum 的优点是我可以进行按位运算/测试。

以下是一个示例和自定义编码器。请注意 Enum 有效,但我假设 IntEnum 在传递给默认函数之前被捕获。

class TimeMode(Enum):
    NTSC = 0
    PAL = 1
    Film = 2

class UiFlags(IntEnum):
    Empty = 0
    Clear = 0
    Checked = 1
    Selected = 2
    Shared = 8

class CObjectsEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o, IntEnum):
            return {'__class__': o.__class__.__name__,
                    '__value__': (o.value,)}
        if isinstance(o, Enum):
            return {'__class__': o.__class__.__name__,
                    '__value__': (o.value,)}
    return json.JSONEncoder.default(self, o)

time_mode = TimeMode.NTSC
ui_flags = UiFlags.Checked
CObjectsEncoder().encode(time_mode)   
CObjectsEncoder().encode(ui_flags)

结果:

'{"__class__": "TimeMode", "__value__": [0]}' # correct encoding
'UiFlags.Checked' # incorrect encoding

有没有办法让 IntEnum 获得相同的结果?我假设它像 int 一样被预先转换为 str 。如何让编码器获取 IntEnum?

【问题讨论】:

  • 这里的问题是isinstance(ui_flags, int) 为真,这意味着使用了整数的标准编码。您的自定义编码器永远不会被调用,因为内置编码器“知道”如何处理整数。
  • 有没有一种简单的方法可以在不编辑编码、迭代编码方法的情况下绕过这个问题?

标签: python json enums int


【解决方案1】:

这里的问题是isinstance(ui_flags, int) 为真,这意味着使用了整数的标准 编码。您的自定义编码器永远不会被调用,因为内置编码器“知道”如何处理整数:

>>> isinstance(UiFlags.Checked, IntEnum)
True
>>> isinstance(UiFlags.Checked, int)
True

JSONEncoder 级别有解决此问题的方法;你可以预处理你的结构:

def map_intenum(data):
    if isinstance(data, IntEnum):
        return {'__class__': o.__class__.__name__,
                '__value__': (o.value,)}
    if isinstance(data, dict):
        return {k: map_intenum(v) for k, v in data.iteritems()}
    if isinstance(data, (list, tuple)):
        return [map_intenum(v) for v in data]
    return data

并将其应用于您的待编码数据编码为 JSON 之前:

json.dumps(map_intenum(data), cls=CObjectsEncoder)

下一步是覆盖JSONEncoder.iterencode() 并在编码之前应用转换:

class CObjectsEncoder(json.JSONEncoder):
    def iterencode(self, o, _one_shot=False):
        o = map_intenum(o)
        return super(CObjectsEncoder, self).iterencode(o, _one_shot)
    def default(self, o):
        if isinstance(o, Enum):
            return {'__class__': o.__class__.__name__,
                    '__value__': (o.value,)}
        return super(CObjectsEncoder, self).default(o)

【讨论】:

    【解决方案2】:

    我通过在我的 IntEnum 类上实现 __str__ 来解决此问题:

    class UiFlags(IntEnum):
        Empty = 0
        Clear = 0
    
        def __str__(self):
            return str(self.value)
    
    json.dumps({'empty': UiFlags.Empty, 'clear': UiFlags.Clear})
    # {"empty": 0, "clear": 1}
    

    【讨论】:

      猜你喜欢
      • 2016-08-21
      • 2020-10-06
      • 2020-07-05
      • 2011-08-19
      • 2015-08-30
      • 2017-09-03
      • 2011-09-10
      • 2013-07-01
      • 2021-11-16
      相关资源
      最近更新 更多