Python实现JSON反序列化类对象的示例
我们的网络协议一般是把数据转换成JSON之后再传输。之前在Java里面,实现序列化和反序列化,不管是jackson,还是fastjson都非常的简单。现在有项目需要用Python来开发,很自然的希望这样的便利也能在Python中体现。
但是在网上看了一些教程,讲反序列化的时候,基本都是转换为dict或者array。这种编程方式我从情感上是无法接受的。难道是这些JSON库都不支持反序列化为类对象?我马上打消了这个念头,Python这样强大的脚本语言,不可能没有完善的JSON库。
于是我就研究了一下原生的json,以及第三方的demjson和simplejson。
一、原生json
我仔细研究了原生json的loads方法的定义
defloads(s,encoding=None,cls=None,object_hook=None,parse_float=None,parse_int=None,parse_constant=None,object_pairs_hook=None,**kw)
这里面的object_hook和object_pairs_hook参数引起了我的注意,我重点说一下object_hook。
官方文档的说明如下:
object_hookisanoptionalfunctionthatwillbecalledwiththeresultofanyobjectliteraldecoded(adict).Thereturnvalueofobject_hookwillbeusedinsteadofthedict.Thisfeaturecanbeusedtoimplementcustomdecoders(e.g.JSON-RPCclasshinting).
这个object_hook根据文档的解释就是一个自定义解码函数,入参数标准反序列化后的dict,我们可以根据自己的规则转换输出为想要的格式。
我又去搜了一下object_hook,大家对于这个东西的处理方式基本就是用一个静态方法把dict转换成对象。
我们的数据结构是这样的
{"status":1,"info":"发布成功","data":{"id":"52","feed_id":"70"}}
于是我就写了这样的代码:
classResponse: def__init__(self,status,info,data)->None: super().__init__() self.status=status self.info=info self.data=data @staticmethod defobject_hook(d): returnResponse(d['status'],d['info'],d['data']) ... resp=json.loads(body,object_hook=Response.object_hook)
一开始呢,确实没有问题,虽然用起来没有java的json库辣么方便,但总归实现了需求。
好景不长,我测试的第一个接口返回的数据中,data是字段一个字符串,反序列化正常。可是后来当接口返回的结构中data字段是一个dict结构的时候,object_hook的入参居然变成了data字段转换之后的dict({"id":"52","feed_id":"70"}),而不是完整的数据。
这些懵逼了,上网搜索了一圈没有结论。于是上网搜了一圈,也没有结论。好吧,我最后又回到官方文档,readthefuckingofficialdocument。
不看不知道,一看吓一跳,官方文档用了一种巧妙的方式实现了上面的需求。
>>>classJSONObject: ...def__init__(self,d): ...self.__dict__=d ... >>> >>>data=json.loads(s,object_hook=JSONObject) >>>data.name 'ACME' >>>data.shares 50 >>>data.price 490.1 >>>
我服了,把json解析之后的dict直接赋值给对象的属性dict,然后就可以随心所欲的使用属性了,真心方便,动态语言就是好。
以上是官方的json库实现方案,那另外两个知名的第三方库呢?
二、demjson
demjson也支持hook。有两种配置的方式:decode函数配置和set_hook函数配置
1.decode
defdecode(txt,encoding=None,**kwargs)
decode函数可以指定很多参数,其中就包括hook函数。hook函数的指定是使用键值对的方式,键是hook函数的名称,值是hook函数。
demjson是通过名字来管理hook函数的,所以hookname不是随便指定的,必须是内置的几种hook函数的名称。
- decode_number
- decode_float
- decode_object
- decode_array
- decode_string
- encode_value
- encode_dict
- encode_dict_key
- encode_sequence
- encode_bytes
- encode_default
demjson.decode(body,encode='utf-8',decode_obbject=Reponse.object_hook)
结果并没有让我很开森,依然是无法处理嵌套结构。日志中显示如下内容:
2018-01-3016:01:17,137poster.pypost_all73INFO :{"status":1,"info":"\u53d1\u5e03\u6210\u529f","data":{"id":"54","feed_id":"72"}}
2018-01-3016:01:17,138response.pyobject_hook13INFO :{'id':'54','feed_id':'72'}
2018-01-3016:01:17,138response.pyobject_hook13INFO :{'status':1,'info':'发布成功','data':demjson.undefined}
很奇怪的是object_hook函数被调用了两次,第一次是data字段的内容,第二是全部的内容,但是data字段没有解析出来。非常奇怪,百思不得其解!!!
2.set_hook
set_hook函数跟上面的decode函数不一样,它是JSON类的成员函数,而decode函数是个静态函数。
defset_hook(self,hookname,function)
吸取之前的教训,这次我仔细阅读了demjson的文档,还真发现点东西。
Netstedvalues.WhendecodingJSONthathasnestedobjectsorarrays,thedecodinghookswillbecalledonceforeverycorrespondingvalue,evenifnested.Generallythedecodinghookswillbecalledfromtheinner-mostvalueoutward,andthenlefttoright.
这里重点说到嵌套的问题,出现嵌套的时候,每个对应的类型都会调用hook函数一次,而且是从最内层,从左往右。好吧,之前出现的问题全部明白了,原来都是这个规则惹的祸,但是为什么这样设计我暂时还是不明白。
set_hook的使用方式
j=demjson.JSON() j.set_hook('decode_array',my_sort_array) j.decode(body,encode='utf-8')
三、simplejson
前面说了那么多,simplejson的方式就没什么可说的,跟官方的json库hook方式一致。
总结
虽然我的需求是满足了,但是还是有一个大大的问号留在我心中,为什么是这样设计,网上没有找到合适的答案,剩下的需要研究源代码分析了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。