假设我具有这样的类层次结构:
class SerializableWidget(object):
# some code
class WidgetA(SerilizableWidget):
# some code
class WidgetB(SerilizableWidget):
# some code
我希望能够将WidgetA
和WidgetB
(以及可能的其他窗口小部件)的实例作为json
序列化为文本文件。然后,我希望能够反序列化它们,而无需事先知道它们的特定类:
some_widget = deserielize_from_file(file_path) # pseudocode, doesn't have to be exactly a method like this
和some_widget
必须从SerilizableWidget
的精确子类构造而成。我该怎么做呢?在我的层次结构的每个类中,到底需要重写/实现哪些方法?
假定以上类的所有字段均为原始类型。如何覆盖某些__to_json__
和__from_json__
方法,诸如此类?
答案 0 :(得分:1)
您可以使用多种方法解决此问题。一个示例是将object_hook
和default
参数分别用于json.load
和json.dump
。
您需要做的就是将类与对象的序列化版本一起存储,然后在加载时必须使用哪个类与哪个名称匹配的映射。
下面的示例使用dispatcher
类装饰器在序列化时存储类名和对象,并在反序列化时稍后查找。您只需要在每个类上使用_as_dict
方法即可将数据转换为字典:
import json
@dispatcher
class Parent(object):
def __init__(self, name):
self.name = name
def _as_dict(self):
return {'name': self.name}
@dispatcher
class Child1(Parent):
def __init__(self, name, n=0):
super().__init__(name)
self.n = n
def _as_dict(self):
d = super()._as_dict()
d['n'] = self.n
return d
@dispatcher
class Child2(Parent):
def __init__(self, name, k='ok'):
super().__init__(name)
self.k = k
def _as_dict(self):
d = super()._as_dict()
d['k'] = self.k
return d
现在进行测试。首先,让我们创建一个包含3个不同类型对象的列表。
>>> obj = [Parent('foo'), Child1('bar', 15), Child2('baz', 'works')]
序列化它会在每个对象中产生带有类名的数据:
>>> s = json.dumps(obj, default=dispatcher.encoder_default)
>>> print(s)
[
{"__class__": "Parent", "name": "foo"},
{"__class__": "Child1", "name": "bar", "n": 15},
{"__class__": "Child2", "name": "baz", "k": "works"}
]
然后将其重新加载会生成正确的对象
obj2 = json.loads(s, object_hook=dispatcher.decoder_hook)
print(obj2)
[
<__main__.Parent object at 0x7fb6cd561cf8>,
<__main__.Child1 object at 0x7fb6cd561d68>,
<__main__.Child2 object at 0x7fb6cd561e10>
]
最后,这是dispatcher
的实现:
class _Dispatcher:
def __init__(self, classname_key='__class__'):
self._key = classname_key
self._classes = {} # to keep a reference to the classes used
def __call__(self, class_): # decorate a class
self._classes[class_.__name__] = class_
return class_
def decoder_hook(self, d):
classname = d.pop(self._key, None)
if classname:
return self._classes[classname](**d)
return d
def encoder_default(self, obj):
d = obj._as_dict()
d[self._key] = type(obj).__name__
return d
dispatcher = _Dispatcher()