利用Python实现Json序列化库的方法步骤


Posted in Python onSeptember 09, 2020

前言

在Python的世界里,将一个对象以json格式进行序列化或反序列化一直是一个问题。Python标准库里面提供了json序列化的工具,我们可以简单的用json.dumps来将一个对象序列化。但是这种序列化仅支持python内置的基本类型。

利用Python实现Json序列化库的方法步骤
Python

在Python的世界里,将一个对象以json格式进行序列化或反序列化一直是一个问题。Python标准库里面提供了json序列化的工具,我们可以简单的用json.dumps来将一个对象序列化。但是这种序列化仅支持python内置的基本类型,对于自定义的类,我们将得到Object of type A is not JSON serializable的错误。

有很多种方法可以用来支持这种序列化,这里有一个很长的关于这个问题的讨论。总结起来,基本上有两种还不错的思路:

  1. 利用标准库的接口:从python标准json库中的JSONDecoder继承,然后自定义实现一个default方法用来自定义序列化过程
  2. 利用第三方库实现:如jsonpickle jsonweb json-tricks等

利用标准库的接口的问题在于,我们需要对每一个自定义类都实现一个JSONDecoder.default接口,难以实现代码复用。

利用第三方库,对我们的代码倒是没有任何侵入性,特别是jsonpickle,由于它是基于pickle标准序列化库实现,可以实现像pickle一样序列化任何对象,一行代码都不需要修改。

但是我们观察这类第三方库的输出的时候,会发现所有的这些类库都会在输出的json中增加一个特殊的标明对象类型的属性。这是为什么呢?Python是一门动态类型的语言,我们无法在对象还没有开始构建的时候知道对象的某一属性的类型信息,为了对反序列化提供支持,看起来确实是不得不这么做。

有人可能觉得这也无可厚非,似乎不影响使用。但是在跨语言通信的时候,这就成为了一个比较麻烦的问题。比如我们有一个Python实现的API,客户端发送了一个json请求过来,我们想在统一的一个地方将json反序列化为我们Python代码的对象。由于客户端不知道服务器端的类型信息,json请求里面就没法加入这样的类型信息,这也就导致这样的类库在反序列化的时候遇到问题。

能不能有一个相对完美的实现呢?先看一下我们理想的json序列化库的需求:

  1. 我们希望能简单的序列化任意自定义对象,只添加一行代码,或者不加入任何代码
  2. 我们希望序列化的结果不加入任何非预期的属性
  3. 我们希望能按照指定的类型进行反序列化,能自动处理嵌套的自定义类,只需要自定义类提供非常简单的支持,或者不需要提供任何支持
  4. 我们希望反序列化的时候能很好的处理属性不存在的情况,以便在我们加入某一属性的时候,可以设置默认值,使得旧版本的序列化结果可以正确的反序列化出来

如果有一个json库能支持上面的四点,那就基本是比较好用的库了。下面我们来尝试实现一下这个类库。

对于我们想要实现的几个需求,我们可以建立下面这样的测试来表达我们所期望的库的API设计:

class A(JsonSerializable):

def __init__(self, a, b):
super().__init__()
self.a = a
self.b = b if b is not None else B(0)

@property
def id(self):
return self.a

def _deserialize_prop(self, name, deserialized):
if name == 'b':
self.b = B.deserialize(deserialized)
return
super()._deserialize_prop(name, deserialized)

class B(JsonSerializable):

def __init__(self, b):
super().__init__()
self.b = b

class JsonSerializableTest(unittest.TestCase):

def test_model_should_serialize_correctly(self):
self.assertEqual(json.dumps({'a': 1, 'b': {'b': 2}}), A(1, B(2)).serialize())

def test_model_should_deserialize_correctly(self):
a = A.deserialize(json.dumps({'a': 1, 'b': {'b': 2}}))
self.assertEqual(1, a.a)
self.assertEqual(2, a.b.b)

def test_model_should_deserialize_with_default_value_correctly(self):
a = A.deserialize(json.dumps({'a': 1}))
self.assertEqual(1, a.a)
self.assertEqual(0, a.b.b)

这里我们希望通过继承的方式来添加支持,这将在反序列化的时候提供一个好处。因为有了它我们就可以直接使用A.deserialize方法来反序列化,而不需要提供任何其他的反序列化函数参数,比如这样json.deserialize(serialized_str, A)。

同时为了验证我们的框架不会将@property属性序列化或者反序列化,我们特意在类A中添加了这样一个属性。

由于在反序列化的时候,框架是无法知道某一个对象属性的类型信息,比如测试中的A.b,为了能正确的反序列化,我们需要提供一点简单的支持,这里我们在类A中覆盖实现了一个父类的方法_deserialize_prop对属性b的反序列化提供支持。

当我们要反序列化一个之前版本的序列化结果时,我们希望能正确的反序列化并使用我们提供的默认值作为最终的反序列化值。这在属性A.b的测试中得到了体现。

(上面的测试有很多边界的情况、支持的变量类型并没有覆盖,此测试只是作为示例使用。)

如果能有一个类可以让上面的测试通过,相信那个类就是我们所需要的类了。这样的类可以实现为如下:

def is_normal_prop(obj, key):
is_prop = isinstance(getattr(type(obj), key, None), property)
is_func_attr = callable(getattr(obj, key))
is_private_attr = key.startswith('__')
return not (is_func_attr or is_prop or is_private_attr)

def is_basic_type(value):
return value is None or type(value) in [int, float, str, bool]

class JsonSerializable:

def _serialize_prop(self, name):
return getattr(self, name)

def _as_dict(self):
props = {}
for key in dir(self):
if not is_normal_prop(self, key):
continue
value = self._serialize_prop(key)
if not (is_basic_type(value) or isinstance(value, JsonSerializable)):
raise Exception('unknown value to serialize to dict: key={}, value={}'.format(key, value))
props[key] = value if is_basic_type(value) else value._as_dict()
return props

def serialize(self):
return json.dumps(self._as_dict(), ensure_ascii=False)

def _deserialize_prop(self, name, deserialized):
setattr(self, name, deserialized)

@classmethod
def deserialize(cls, json_encoded):
if json_encoded is None:
return None

args = inspect.getfullargspec(cls)
args_without_self = args.args[1:]
obj = cls(*([None] * len(args_without_self)))

data = json.loads(json_encoded, encoding='utf8') if type(json_encoded) is str else json_encoded
for key in dir(obj):
if not is_normal_prop(obj, key):
continue
if key in data:
obj._deserialize_prop(key, data[key])
return obj

在实现时,我们利用了Python的内省机制,这样就可以自动的识别对象的属性及运行时类型了。当然对于这个简单的类还有很多待支持的功能,使用上也有很多限制,比如:

  1. 当某一属性为自定义类的类型的时候,需要子类覆盖实现_deserialize_prop方法为反序列化过程提供支持
  2. 当某一属性为由自定义类构成的一个list tuple dict复杂对象时,需要子类覆盖实现_deserialize_prop方法为反序列化过程提供支持
  3. 简单属性必须为python内置的基础类型,比如如果某一属性的类型为numpy.float64,序列化反序列化将不能正常工作

虽然有上述限制,但是这正好要求我们在做模型设计的时候保持克制,不要将某一个对象设计得过于复杂。比如如果有属性为dict类型,我们可以将这个dict抽象为另一个自定义类型,然后用类型嵌套的方式来实现。

到这里这个基类就差不多可以支撑我们日常的开发需要了。当然对于这个简单的实现还有可能有其他的需求或者问题,大家如有发现,欢迎留言交流。

总结

到此这篇关于利用Python实现Json序列化库的文章就介绍到这了,更多相关Python实现Json序列化库内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python基础教程之lambda表达式使用方法
Feb 12 Python
Python+tkinter使用40行代码实现计算器功能
Jan 30 Python
Python中一些不为人知的基础技巧总结
May 19 Python
对python创建及引用动态变量名的示例讲解
Nov 10 Python
Python编程在flask中模拟进行Restful的CRUD操作
Dec 28 Python
详解Python装饰器
Mar 25 Python
python机器学习包mlxtend的安装和配置详解
Aug 21 Python
解决python彩色螺旋线绘制引发的问题
Nov 23 Python
Python利用Scrapy框架爬取豆瓣电影示例
Jan 17 Python
Django中modelform组件实例用法总结
Feb 10 Python
python中逻辑与或(and、or)和按位与或异或(&、|、^)区别
Aug 05 Python
jupyter notebook 写代码自动补全的实现
Nov 02 Python
python之语音识别speech模块
Sep 09 #Python
python speech模块的使用方法
Sep 09 #Python
python计算auc的方法
Sep 09 #Python
详解anaconda离线安装pytorchGPU版
Sep 08 #Python
python如何将图片转换素描画
Sep 08 #Python
Python自动化之UnitTest框架实战记录
Sep 08 #Python
Python Opencv实现单目标检测的示例代码
Sep 08 #Python
You might like
php使用curl模拟登录后采集页面的例子
2013/11/04 PHP
驱动事件的addEvent.js代码
2007/03/27 Javascript
各种常用浏览器getBoundingClientRect的解析
2009/05/21 Javascript
jQuery html()等方法介绍
2009/11/18 Javascript
JS保留两位小数 四舍五入函数的小例子
2013/11/20 Javascript
浅析AngularJs HTTP响应拦截器
2015/12/28 Javascript
jQuery时间插件jquery.clock.js用法实例(5个示例)
2016/01/14 Javascript
jQuery添加和删除输入文本框标签代码
2016/05/20 Javascript
jQuery简单实现彩色云标签效果示例
2016/08/01 Javascript
JavaScript触发onScroll事件的函数节流详解
2016/12/14 Javascript
js获取元素下的第一级子元素的方法(推荐)
2017/03/05 Javascript
JavaScript使用原型和原型链实现对象继承的方法详解
2017/04/05 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
2017/06/28 Javascript
jquery鼠标悬停导航下划线滑出效果
2017/09/29 jQuery
js原生日历的实例(推荐)
2017/10/31 Javascript
JS实现的计数排序与基数排序算法示例
2017/12/04 Javascript
在vue中使用image-webpack-loader实例
2020/11/12 Javascript
如何利用JavaScript编写一个格斗小游戏
2021/01/06 Javascript
实例解析Python中的__new__特殊方法
2016/06/02 Python
Python中单例模式总结
2018/02/20 Python
pandas把dataframe转成Series,改变列中值的类型方法
2018/04/10 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
OpenCV模板匹配matchTemplate的实现
2019/10/18 Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
2019/12/27 Python
HTML5和CSS3让网页设计提升到下一个高度
2009/08/14 HTML / CSS
用css3实现当鼠标移进去时当前亮其他变灰效果
2014/04/08 HTML / CSS
自动化专业个人求职信范文
2013/12/30 职场文书
亲戚结婚的请假条
2014/02/11 职场文书
团队口号大全
2014/06/06 职场文书
2014年保洁员工作总结
2014/11/19 职场文书
精神文明建设先进个人事迹材料
2014/12/24 职场文书
2015年教师党员公开承诺书
2015/01/22 职场文书
2016年植树节红领巾广播稿
2015/12/17 职场文书
教师网络培训心得体会
2016/01/09 职场文书
使用Pytorch实现two-head(多输出)模型的操作
2021/05/28 Python
Python读写yaml文件
2022/03/20 Python