详解Python对JSON中的特殊类型进行Encoder
Python处理JSON数据时,dumps函数是经常用到的,当JSON数据中有特殊类型时,往往是比较头疼的,因为经常会报这样一个错误。
自定义编码类
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) importjson fromdatetimeimportdatetime USER_DATA=dict( id=1,name='wxnacy',ts=datetime.now() ) print(json.dumps(USER_DATA))
Traceback(mostrecentcalllast): File"/Users/wxnacy/PycharmProjects/study/python/office_module/json_demo/dumps.py",line74,indumps_encoder() File"/Users/wxnacy/PycharmProjects/study/python/office_module/json_demo/dumps.py",line68,indumps_encoder print(json.dumps(USER_DATA)) File"/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py",line231,indumps return_default_encoder.encode(obj) File"/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line199,inencode chunks=self.iterencode(o,_one_shot=True) File"/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line257,initerencode return_iterencode(o,0) File"/Users/wxnacy/.pyenv/versions/3.6.0/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line180,indefault o.__class__.__name__) TypeError:Objectoftype'datetime'isnotJSONserializable
原因在于 dumps函数不知道如何处理 datetime对象,默认情况下 json模块使用 json.JSONEncoder类来进行编码,此时我们需要自定义一下编码类。
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) classCustomEncoder(json.JSONEncoder): defdefault(self,x): ifisinstance(x,datetime): returnint(x.timestamp()) returnsuper().default(self,x)
定义编码类 CustomEncoder并重写实例的 default函数,对特殊类型进行处理,其余类型继续使用父类的解析。
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) importjson fromdatetimeimportdatetime classCustomEncoder(json.JSONEncoder): defdefault(self,x): ifisinstance(x,datetime): returnint(x.timestamp()) returnsuper().default(self,x) USER_DATA=dict( id=1,name='wxnacy',ts=datetime.now() ) print(json.dumps(USER_DATA,cls=CustomEncoder)) #{"id":1,"name":"wxnacy","ts":1562938926}
最后整合起来,将类使用 cls参数传入 dumps函数即可。
使用 CustomEncoder实例的 encode函数可以对对象进行转码
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) print(CustomEncoder().encode(datetime.now())) #1562939035
在父类源码中,所有的编码逻辑都在 encode函数中, default只负责抛出 TypeError异常,这就是文章开始报错的出处。
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) defdefault(self,o): """Implementthismethodinasubclasssuchthatitreturns aserializableobjectfor``o``,orcallsthebaseimplementation (toraisea``TypeError``). Forexample,tosupportarbitraryiterators,youcould implementdefaultlikethis:: defdefault(self,o): try: iterable=iter(o) exceptTypeError: pass else: returnlist(iterable) #LetthebaseclassdefaultmethodraisetheTypeError returnJSONEncoder.default(self,o) """ raiseTypeError(f'Objectoftype{o.__class__.__name__}' f'isnotJSONserializable') defencode(self,o): """ReturnaJSONstringrepresentationofaPythondatastructure. >>>fromjson.encoderimportJSONEncoder >>>JSONEncoder().encode({"foo":["bar","baz"]}) '{"foo":["bar","baz"]}' """ #Thisisforextremelysimplecasesandbenchmarks. ifisinstance(o,str): ifself.ensure_ascii: returnencode_basestring_ascii(o) else: returnencode_basestring(o) #Thisdoesn'tpasstheiteratordirectlyto''.join()becausethe #exceptionsaren'tasdetailed.Thelistcallshouldberoughly #equivalenttothePySequence_Fastthat''.join()woulddo. chunks=self.iterencode(o,_one_shot=True) ifnotisinstance(chunks,(list,tuple)): chunks=list(chunks) return''.join(chunks)
单分派装饰器处理对象
CustomEncoder如果处理的对象种类很多的话,需要写多个 ifelifelse来区分,这样并不是不行,但是不够优雅,不够pythonic
根据对象的类型不同,而做出不同的处理。刚好有个装饰器可以做到这点,它就是单分派函数functools.singledispatch
#!/usr/bin/envpython #-*-coding:utf-8-*- #Author:wxnacy(wxnacy@gmail.com) fromdatetimeimportdatetime fromdatetimeimportdate fromfunctoolsimportsingledispatch classCustomEncoder(json.JSONEncoder): defdefault(self,x): try: returnencode(x) exceptTypeError: returnsuper().default(self,x) @singledispatch#1 defencode(x): raiseTypeError('Unencodetype') @encode.register(datetime)#2 def_(x): returnint(x.timestamp()) @encode.register(date) def_(x): returnx.isoformat() print(json.dumps(dict(dt=datetime.now(),d=date.today()),cls=CustomEncoder)) #{"dt":1562940781,"d":"2019-07-12"}
1使用 @singledispatch装饰 encode函数,是他处理默认类型。同时给他添加一个装饰器构造函数变量。
2`@encode.register() 是一个装饰器构造函数,接收需要处理的对象类型作为参数。用它装饰的函数不需要名字,_`代替即可。
最后提一点, json也可以在命令行中使用
$echo'{"json":"obj"}'|python-mjson.tool { "json":"obj" }
参考链接
json
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。