详解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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。