序列化和反序列化来自用户定义类的对象
Posted
技术标签:
【中文标题】序列化和反序列化来自用户定义类的对象【英文标题】:Serialize and deserialize objects from user-defined classes 【发布时间】:2019-01-29 05:38:14 【问题描述】:假设我有这样的类层次结构:
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__
方法,诸如此类?
【问题讨论】:
谁认为这太宽泛而低估了我?我怎样才能使它更具体?它没有生产力! 你看过像Marshmallow这样的库吗? 为什么我需要一个额外的库,我可以不直接调用 json.dumps() 和 json.load() 吗?我需要做的就是在我的类中实现正确的方法。我错过了什么? 您并不严格需要第三方库,但序列化比您想象的要复杂得多(因此存在 Marshmallow 和其他类似库)。我建议查看我提供的链接并做出自己的决定。例如,Marshmallow 希望您定义模式,而 Django REST 框架希望您构建序列化程序。为什么你认为这两个都让你做体力活? 您知道我将如何在上述设置中执行此操作吗?假设这些类的所有字段都是原始类型。谢谢 【参考方案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()
【讨论】:
谢谢,正是我需要的!夫妻跟进:1)需要调度员吗? 2)我可以用jsonpickle达到同样的效果吗,优缺点是什么? @BaronYugovichdispatcher
只是可能的方法之一。我喜欢使用它,因为它清楚地说明了添加了哪些类以及添加的原因 - 它限制了 json 文件可以做什么。你装饰你想要的类,没有人可以用我不打算“公开”的类手工制作一个 json 文件。我对jsonpickle
没有任何经验,我不在我的项目中使用它的原因是它“太神奇了”......我喜欢明确的东西,比如定义我想要序列化的属性以及我如何想要它们在 json 文件中。也就是说,jsonpickle
可能使用了与此示例类似的方法。
@user2880391 from some_module import dispatcher
如果你把它放在some_module.py
中以上是关于序列化和反序列化来自用户定义类的对象的主要内容,如果未能解决你的问题,请参考以下文章