【问题标题】:Python: How to decode enum type from jsonPython:如何从 json 解码枚举类型
【发布时间】:2015-06-24 18:40:25
【问题描述】:
class MSG_TYPE(IntEnum):
    REQUEST = 0
    GRANT = 1
    RELEASE = 2
    FAIL = 3
    INQUIRE = 4
    YIELD = 5

    def __json__(self):
        return str(self)

class MessageEncoder(JSONEncoder):
    def default(self, obj):
        return obj.__json__()

class Message(object):
    def __init__(self, msg_type, src, dest, data):
        self.msg_type = msg_type
        self.src = src
        self.dest = dest
        self.data = data

    def __json__(self):
        return dict (\
            msg_type=self.msg_type, \
            src=self.src, \
            dest=self.dest, \
            data=self.data,\
            )

    def ToJSON(self):
        return json.dumps(self, cls=MessageEncoder)

msg = Message(msg_type=MSG_TYPE.FAIL, src=0, dest=1, data="hello world")
encoded_msg = msg.ToJSON()
decoded_msg = yaml.load(encoded_msg)
print type(decoded_msg['msg_type'])

调用print type(decoded_msg['msg_type']) 时,我得到的结果是<type 'str'>,而不是原来的MSG_TYPTE 类型。我觉得我还应该编写一个自定义的 json 解码器,但有点困惑如何做到这一点。有任何想法吗?谢谢。

【问题讨论】:

  • 顺便说一句,在,之后你不需要\

标签: python json enums messages


【解决方案1】:

当调用 print type(decoded_msg['msg_type']) 时,我得到的不是原来的 MSG_TYPTE 类型。

嗯,是的,那是因为你告诉MSG_TYPE 像这样编码自己:

def __json__(self):
    return str(self)

所以,这显然会解码回一个字符串。如果你不想这样,想出一些独特的方式来对值进行编码,而不是仅仅对它们的字符串表示进行编码。

最常见的方法是使用object 的某种特殊形式对所有自定义类型(包括枚举类型)进行编码,就像您为Message 所做的那样。例如,您可以在编码对象类型的object 中放置一个py-type 字段,然后其他字段的含义都取决于类型。理想情况下,您当然希望抽象出共同点,而不是对同一事物进行 100 次硬编码。


我觉得我也应该编写一个自定义的 json 解码器,但有点困惑如何做到这一点。

嗯,你读过the documentation吗?你到底在哪里困惑?通过对 StackOverflow 问题进行后续跟进,您不会获得完整的教程……

假设您的所有类型都有一个特殊的object 结构,您可以使用object_hook 将值解码回原始值。例如,作为一个快速破解:

class MessageEncoder(JSONEncoder):
    def default(self, obj):
        return {'py-type': type(obj).__name__, 'value': obj.__json__()}

class MessageDecoder(JSONDecoder):
    def __init__(self, hook=None, *args, **kwargs):
        if hook is None: hook = self.hook
        return super().__init__(hook, *args, **kwargs)
    def hook(self, obj):
        if isinstance(obj, dict):
            pytype = obj.get('py-type')
            if pytype:
                t = globals()[pytype]
                return t.__unjson__(**obj['value'])
        return obj

现在,在您的 Message 课程中:

@classmethod
def __unjson__(cls, msg_type, src, dest, data):
    return cls(msg_type, src, dest, data)

你需要一个 MSG_TYPE.__json__ 来返回一个字典,也许只是 {'name': str(self)},然后是一个 __unjson__ 来做类似 getattr(cls, name) 的事情。

现实生活中的解决方案可能应该让类自己注册而不是通过名称来查找它们,或者应该通过限定名称来处理它们而不是仅仅去globals()。而且您可能希望让事物编码为 object 以外的其他内容,或者,如果没有,则将 py-type 塞入对象中,而不是将其包装在另一个对象中。并且可能还有其他方法可以使 JSON 更紧凑和/或可读性更高。一点点错误处理会很好。以此类推。


您可能想查看jsonpickle 的实现——不是因为您想做与它完全相同的事情,而是想看看它是如何连接所有部分的。

【讨论】:

    【解决方案2】:

    在这种情况下,覆盖编码器的默认方法无关紧要,因为您的对象永远不会被传递给该方法。它被视为 int。

    如果您自己运行编码器:

    msg_type = MSG_TYPE.RELEASE
    MessageEncoder().encode(msg_type)
    

    你会得到:

    'MSG_TYPE.RELEASE'
    

    如果可以,请使用 Enum,您应该不会有任何问题。我也问了一个类似的问题:

    How do I serialize IntEnum from enum34 to json in python?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-11
      • 2021-07-15
      • 1970-01-01
      • 2018-10-18
      • 2017-01-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多