Python:如何从 json 解码枚举类型
Python: How to decode enum type from json
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 解码器,但有点困惑如何去做。有任何想法吗?谢谢。
When calling print type(decoded_msg['msg_type']), I get the result instead of the original MSG_TYPTE type.
嗯,是的,那是因为你告诉 MSG_TYPE
像这样编码自己:
def __json__(self):
return str(self)
所以,这显然要解码回字符串。如果你不想要那样,想出一些独特的方法来编码值,而不是仅仅编码它们的字符串表示。
最常用的方法是使用 object
的某种特殊形式对所有自定义类型(包括枚举类型)进行编码——就像您为 Message
所做的一样。例如,您可以在 object
中放置一个 py-type
字段,它对对象的类型进行编码,然后其他字段的含义都取决于类型。理想情况下,您当然希望抽象出共性,而不是对同一事物进行 100 次硬编码。
I feel like I should also write a custom json decoder but kind of confused how to do that.
嗯,你读过the documentation了吗?你到底哪里糊涂了?您不会通过跟踪 Whosebug 问题来获得完整的教程……
假设您的所有类型都有一个特殊的 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
class:
@classmethod
def __unjson__(cls, msg_type, src, dest, data):
return cls(msg_type, src, dest, data)
你需要一个 MSG_TYPE.__json__
那个 returns 一个字典,也许只是 {'name': str(self)}
,然后是一个 __unjson__
做类似 getattr(cls, name)
.
现实生活中的解决方案可能应该让 classes 自己注册而不是按名称查找它们,或者应该处理按限定名称查找它们而不是直接去 globals()
.你可能想让事物编码成 object
以外的东西——或者,如果不是,只是将 py-type
塞进对象而不是将它包装在另一个对象中。并且可能还有其他方法可以使 JSON 更紧凑 and/or 可读。一点点错误处理会很好。等等。
您可能想看看 jsonpickle
的实现 — 不是因为您想做与它完全相同的事情,而是想看看它是如何连接所有部分的。
在这种情况下覆盖编码器的默认方法无关紧要,因为您的对象永远不会传递给该方法。它被视为一个整数。
如果您 运行 编码器本身:
msg_type = MSG_TYPE.RELEASE
MessageEncoder().encode(msg_type)
您将获得:
'MSG_TYPE.RELEASE'
如果可以,请使用枚举,应该不会有任何问题。我也问过类似的问题:
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 解码器,但有点困惑如何去做。有任何想法吗?谢谢。
When calling print type(decoded_msg['msg_type']), I get the result instead of the original MSG_TYPTE type.
嗯,是的,那是因为你告诉 MSG_TYPE
像这样编码自己:
def __json__(self):
return str(self)
所以,这显然要解码回字符串。如果你不想要那样,想出一些独特的方法来编码值,而不是仅仅编码它们的字符串表示。
最常用的方法是使用 object
的某种特殊形式对所有自定义类型(包括枚举类型)进行编码——就像您为 Message
所做的一样。例如,您可以在 object
中放置一个 py-type
字段,它对对象的类型进行编码,然后其他字段的含义都取决于类型。理想情况下,您当然希望抽象出共性,而不是对同一事物进行 100 次硬编码。
I feel like I should also write a custom json decoder but kind of confused how to do that.
嗯,你读过the documentation了吗?你到底哪里糊涂了?您不会通过跟踪 Whosebug 问题来获得完整的教程……
假设您的所有类型都有一个特殊的 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
class:
@classmethod
def __unjson__(cls, msg_type, src, dest, data):
return cls(msg_type, src, dest, data)
你需要一个 MSG_TYPE.__json__
那个 returns 一个字典,也许只是 {'name': str(self)}
,然后是一个 __unjson__
做类似 getattr(cls, name)
.
现实生活中的解决方案可能应该让 classes 自己注册而不是按名称查找它们,或者应该处理按限定名称查找它们而不是直接去 globals()
.你可能想让事物编码成 object
以外的东西——或者,如果不是,只是将 py-type
塞进对象而不是将它包装在另一个对象中。并且可能还有其他方法可以使 JSON 更紧凑 and/or 可读。一点点错误处理会很好。等等。
您可能想看看 jsonpickle
的实现 — 不是因为您想做与它完全相同的事情,而是想看看它是如何连接所有部分的。
在这种情况下覆盖编码器的默认方法无关紧要,因为您的对象永远不会传递给该方法。它被视为一个整数。
如果您 运行 编码器本身:
msg_type = MSG_TYPE.RELEASE
MessageEncoder().encode(msg_type)
您将获得:
'MSG_TYPE.RELEASE'
如果可以,请使用枚举,应该不会有任何问题。我也问过类似的问题: