问题及解决方案
jsonpickle是比较常用的序列化模块, 其特点是对于复杂的对象基本上都能够很好的序列化, 适用范围比较广.
有时候我们有需求, 希望私有变量能够不被序列化, 但是使用jsonpickle的自定义定制机制实现起来不太方便, 可以采用如下的方式:
class NoSerailPrivates:'''表示不序列化私有变量, 以_开头都变量'''def __getstate__(self):'''对于jsonpickle模块, 决定序列化哪些属性, 以'_'开头的属性不序列化'''state = self.__dict__.copy()# 创建一个临时副本进行迭代和删除操作temp_dict = state.copy()for key in temp_dict.keys():if key.startswith('_'): del state[key]return statedef __setstate__(self, state):# 在这里执行初始化操作#self.__init__() # 可以调用初始化函数, 初始化那些没有序列化的属性self.__dict__.update(state)
但是这种方式, 需要被序列化对象继承NoSerailPrivates或者在内部实现__getstate__
, __setstate__
, 另外, 如果对象包含子对象, 子对象也要做同样的事情.
当然, 还有别的办法, 比如注册自定义处理器, 但是也需要与被序列化类绑定, 并且对于子对象也需要绑定.
那么有没有办法可以对所有对象默认具有忽略私有成员的功能呢? 我目前还没有找到解决方案. 只能尝试修改源码了, 好在并不是很难.
下面讲一下主要修改步骤:
- 修改
pickler.py
文件中def _flatten_obj_instance(self, obj):
函数. 这个函数负责把一个对象转换为序列化后的字符串.
修改点: 增加一个exclude_privates=True
参数选项, 这个选项是原本没有的, 是我新添加的. 用于后面是否过滤掉私有成员时判断.
return self._flatten_dict_obj(obj.__dict__, data, exclude=exclude, exclude_privates=True)
- 修改
pickler.py
文件中def _flatten_dict_obj(self, obj, data=None, exclude=(), exclude_privates=False):
函数. 这个函数负责把(k,v)字典转换为序列化后的字符串.
修改点:
2.1 函数增加一个exclude_privates=False
参数选项;
2.2 在下面的代码中增加一个exclude_privates=exclude_privates
参数选项, 这个选项的值继承自此函数的传入参数.
for k, v in util.items(obj, exclude=exclude, exclude_privates=exclude_privates):
- 修改
util.py
文件的def items(obj, exclude=(), exclude_privates=False):
函数. 这个函数负责枚举字典的key,value并进行过滤.
修改点:
3.1 函数增加一个exclude_privates=False
参数选项;
3.2 函数实现代码修改如下, 提供对象私有成员过滤功能:
def items(obj, exclude=(), exclude_privates=False):"""TODO: Replace all calls to this with plain dict.items()"""for k, v in obj.items():if k in exclude:continueif exclude_privates:if isinstance(k,str) and k.startswith('_'):continueyield k, v
最后实现效果
测试代码如下:
import jsonpickleclass InnerClass:def __init__(self):self.public_var = 'hzqtest'self.__private_var = 20class OuterClass:def __init__(self):self.public_var = 100self.__private_var = 200self.inner = InnerClass() # 嵌套对象obj = OuterClass()
#obj = {'hzqtest':10,'_abc':100}
json_str = jsonpickle.encode(obj)
print(json_str) # 输出应仅包含 public_var
输出信息: {"py/object": "__main__.OuterClass", "public_var": 100, "inner": {"py/object": "__main__.InnerClass", "public_var": "hzqtest"}}
由此可见, 对象私有成员被屏蔽.