Python3中的json模块使用详解
1.概述
JSON(JavaScriptObjectNotation)是一种使用广泛的轻量数据格式.Python标准库中的json模块提供了JSON数据的处理功能.
Python中一种非常常用的基本数据结构就是字典(Dictionary).它的典型结构如下:
d={ 'a':123, 'b':{ 'x':['A','B','C'] } }
而JSON的结构如下:
{ "a":123, "b":{ "x":["A","B","C"] } }
可以看到,Dictionary和JSON非常接近,而Python中的json库提供的主要功能,也是两者之间的转换.
2.读取JSON
json.loads方法可以将包含了一个JSON数据的str,bytes或者bytearray对象,转化为一个PythonDictionary.它的完型接口签名如下:
json.loads(s,*,encoding=None,cls=None,object_hook=None,parse_float=None,parse_int=None,parse_constant=None,object_pairs_hook=None,**kw)
2.1最简单的例子
json.loads最基本的使用方式就是将一个包含JSON数据的str传递给这个方法:
>>>json.loads('{"a":123}') {'a':123}
注意
在Python中,str值可以放在一对单引号中,也可以放在一对双引号中:
>>>'ABC'=="ABC" True
所以,在定义Dictionary的str类型的键和值的时候,使用单引号或者双引号都是合法和等价的:
>>>{"a":'ABC'}=={'a':"ABC"} True
但是,在JSON中,字符串数据只能放在双引号中,因而json.loads方法处理的字符串的JSON内容中,字符串必须使用双引号.否则就会发生解码错误:
>>>json.loads("{'a':123}")
Traceback(mostrecentcalllast):
File"",line1,in
File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py",line354,inloads
return_default_decoder.decode(s)
File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py",line339,indecode
obj,end=self.raw_decode(s,idx=_w(s,0).end())
File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py",line355,inraw_decode
obj,end=self.scan_once(s,idx)
json.decoder.JSONDecodeError:Expectingpropertynameenclosedindoublequotes:line1column2(char1)
如果被处理的Python字符串是包含在双引号中的,那么JSON中的双引号就需要转义:
>>>json.loads("{\"a\":123}") {'a':123}
2.2bytes和bytearray数据
对于内容是JSON数据的bytes和bytearray,json.loads方法也可以处理:
>>>json.loads('{"a":123}'.encode('UTF-8')) {'a':123} >>>json.loads(bytearray('{"a":123}','UTF-8')) {'a':123}
2.3编码格式
json.loads的第二个参数是encoding没有实际作用.
由于Python3中str类型总是使用UTF-8编码,所以s参数为str类型时,json.loads方法自动使用UTF-8编码.并且,str不能以BOM字节开头.
当s参数为bytes或者bytearray时,json.loads方法会自动判断为UTF-8,UTF-16还是UTF-32编码.默认也是将其按照UTF-8编码转化为str对象进行后续处理.
2.4数据类型转换
JSON可以表示四种主类型数据
- 1.字符串string
- 2.数字number
- 3.布尔类boolean
- 4.空值null
以及两结数据结构
- 1.对象object
- 2.数组array
默认实现中,JSON和Python之间的数据转换对应关系如下表:
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number(int) | int |
number(real) | float |
true | True |
false | False |
null | None |
实际转换情况如下例:
>>>json.loads(""" ...{ ..."obj":{ ..."str":"ABC", ..."int":123, ..."float":-321.89, ..."bool_true":true, ..."bool_false":false, ..."null":null, ..."array":[1,2,3] ...} ...}""") {'obj':{'str':'ABC','int':123,'float':-321.89,'bool_true':True,'bool_false':False,'null':None,'array':[1,2,3]}}
对于JSON中数字number类型的数据,有以下几点需要注意:
1.JSON中的实数realnumber类型的精度不能超过Python中的float类型的精度范围,否则就有精度损失.如下例:
>>>json.loads('3.141592653589793238462643383279') 3.141592653589793
2.JSON标准不包括非数字NaN,正无穷Infinity和负无穷-Infinity,但是json.loads方法默认会将JSON字符串中的NaN,Infinity,-Infinity转化为Python中的float('nan'),float('inf')和float('-inf').注意,这里JSON中的NaN,Infinity,-Infinity必须大小写正确并且拼写完整.如下例
>>>json.loads('{"inf":Infinity,"nan":NaN,"ninf":-Infinity}') {'inf':inf,'nan':nan,'ninf':-inf}
2.5自定义JSON对象转换类型
json.loads默认将JSON中的对象数据转化为Dictionary类型,object_hook参数可以用来改变构造出的对象.
object_hook接受一个函数,这个函数的输入参数为JSON中对象数据转化出的Dictionary对象,其返回值则为自定义的对象.如下例所示:
>>>classMyJSONObj: ...def__init__(self,x): ...self.x=x ... >>>defmy_json_obj_hook(data): ...print('obj_hookdata:%s'%data) ...returnMyJSONObj(data['x']) ... >>>result=json.loads('{"x":123}',object_hook=my_json_obj_hook) obj_hookdata:{'x':123} >>>type(result)>>>result.x 123
当JSON中的对象有嵌套时,json.loads方法会按照深度优先的方式遍历对象树,将各层的对象数据传递给object_hook.叶节点的JSON对象构造出的Python对象,会作为父节点的一个值,传递给父节点的object_hook方法.如下例:
>>>classMyJSONObj: ...def__init__(self,x,y): ...self.x=x ...self.y=y ... >>>defmy_json_obj_hook(data): ...print('obj_hookdata:%s'%data) ...returnMyJSONObj(**data) ... >>>result=json.loads('{"x":{"x":11,"y":12},"y":{"x":21,"y":22}}',object_hook=my_json_obj_hook) obj_hookdata:{'x':11,'y':12} obj_hookdata:{'x':21,'y':22} obj_hookdata:{'x':<__main__.MyJSONObjobjectat0x10417ef28>,'y':<__main__.MyJSONObjobjectat0x10417ed68>}
除了object_hook参数以外,还有一个object_pairs_hook参数.这个参数同样可以用来改变json.loads方法构造出的Python对象的类型.这个参数和object_hook的不同,在于传入的方法所接收到的输入数据不是一个Dictionary,而是一个包含tuple的list.每个tuple都有两个元素,第一个元素是JSON数据中的键,第二个元素是这个键对应的值.如JSON对象
{ "a":123, "b":"ABC" }
对应的输入数据是
[
('a':123),
('b','ABC')
]
当调用json.loads方法时,同时指定object_hook和object_pairs_hook,object_pairs_hook会覆盖object_hook参数.
2.6自定义JSON数字转换类型
默认实现中,JSON中的实数被转换为Python的float类型,整数被转换为int或者long类型.类似object_hook,我们可以通过parse_float和parse_int参数指定自定义的转换逻辑.这两个方法的输入参数为表示JSON实数或者整数的字符串.下例中,我们将实数转换为numpy.float64,将整数转换为numpy.int64:
>>>defmy_parse_float(f): ...print('%s(%s)'%(type(f),f)) ...returnnumpy.float64(f) ... >>>defmy_parse_int(i): ...print('%s(%s)'%(type(i),i)) ...returnnumpy.int64(i) ... >>>result=json.loads('{"i":123,"f":321.45}',parse_float=my_parse_float,parse_int=my_parse_int)(123) (321.45) >>>type(result['i']) >>>type(result['f'])
2.6.1自定义NaN,Infinity和-Infinity转换类型
由于标准JSON数据不支持NaN,Infinity和-Infinity,所以parse_float并不会接收到这几个值.当需要自定义这几个值转换的对象的时候,就需要使用另外一个接口parse_constant.比如下例中,将这几个值同样转换为numpy.float64类型:
>>>defmy_parse_constant(data): ...print('%s(%s)'%(type(data),data)) ...returnnumpy.float64(data) ... >>>result=json.loads('{"inf":Infinity,"nan":NaN,"ninf":-Infinity}',parse_constant=my_parse_constant)(Infinity) (NaN) (-Infinity) >>>result['inf'] inf >>>type(result['inf'])
2.7非对象顶级值
根据JSON规范,一个JSON数据中,可以只包含一个值,而不是一个完整的对象.这个值可以是一个字符串,一个数字,布尔值,空值,或者一个数组.除了这三种JSON规范中给出的类型,还可以是NaN,Infinity或者-Infinity:
>>>json.loads('"hello"') 'hello' >>>json.loads('123') 123 >>>json.loads('123.34') 123.34 >>>json.loads('true') True >>>json.loads('false') False >>>print(json.loads('null')) None >>>json.loads('[1,2,3]') [1,2,3]
2.8重复键名
在同一层级JSON对象中,不应当出现重复的键名,不过JSON规范中没有给出这种情况的处理标准.在json.loads中,当JSON数据中有重复键名,则后面的键值会覆盖前面的:
>>>json.loads('{"a":123,"b":"ABC","a":321}') {'a':321,'b':'ABC'}
2.9处理JSON数据文件
当JSON数据是保存在一个文件中的时候,json.load方法可以用来从这个文件中读取数据,并转换为Python对象.json.load方法的第一个参数就是指向JSON数据文件的文件类型对象.
比如/tmp/data.json文件的内含如下:
{ "a":123, "b":"ABC" }
可以使用下例中的代码来读取并转化文件中的JSON数据:
>>>withopen('/tmp/data.json')asjf: ...json.load(jf) ... {u'a':123,u'b':u'ABC'}
除了文件类型的对象,只要是实现了read方法的类文件对象,都可以作为fp参数,比如下例中的io.StringIO:
>>>sio=io.StringIO('{"a":123}') >>>json.load(sio) {'a':123}
json.load方法的其他参数的意义和使用方法和上文中的json.loads相同,这里不再赘述.
3生成JSON
json.dumps方法可以将Python对象转换为一个表示JONS数据的字符串.它的完整接口签名如下:
json.dumps(obj,*,skipkeys=False,ensure_ascii=True,check_circular=True,allow_nan=True,cls=None,indent=None,separators=None,default=None,sort_keys=False,**kw)
它的第一个参数obj即为要转换的数据对象.
>>>json.dumps({'a':123,'b':'ABC'}) '{"a":123,"b":"ABC"}'
3.1编码格式
json.dumps的ensure_ascii参数用来控制生成的JSON字符串的编码.其默认值为True,此时,所有的非ASCII码字条都会转义.如果不希望自动进行转义,则会保持原有编码,限UTF-8.如下例所示:
>>>json.dumps({'数字':123,'字符':'一二三'}) '{"\\u6570\\u5b57":123,"\\u5b57\\u7b26":"\\u4e00\\u4e8c\\u4e09"}' >>>json.dumps({'数字':123,'字符':'一二三'},ensure_ascii=False) '{"数字":123,"字符":"一二三"}'
3.2数据类型转换
在默认实现中,json.dumps可以处理的Python对象,及其所有的属性值,类型必须为dict,list,tuple,str,float或者int.这些类型与JSON的数据转换关系如下表:
Python | JSON |
---|---|
dict | object |
list,tuple | array |
str | string |
int,float,int-&float-derivedemuns | number |
True | true |
False | false |
None | null |
实际转换情况如下示例:
>>>json.dumps( ...{ ...'str':'ABC', ...'int':123, ...'float':321.45, ...'bool_true':True, ...'bool_false':False, ...'none':None, ...'list':[1,2,3], ...'tuple':[12,34] ...} ...) '{"str":"ABC","int":123,"float":321.45,"bool_true":true,"bool_flase":false,"none":null,"list":[1,2,3],"tuple":[12,34]}'
虽然JSON标准规范不支持NaN,Infinity和-Infinity,但是json.dumps的默认实现会将float('nan'),float('inf')和float('-inf')转换为常量NaN,Infinity,和-Infinity.如下例所示:
>>>json.dumps( ...{ ...'nan':float('nan'), ...'inf':float('inf'), ...'-inf':float('-inf') ...} ...) '{"nan":NaN,"inf":Infinity,"-inf":-Infinity}'
由于这些常量可能会导致生成的JSON字符串不能被其他的JSON实现处理,为了防止这种情况出现,可以将json.dumps的allow_nan参数设置为True.此时,当处理的Python对象中出现这些值时,json.dumps方法会抛出异常.
3.3循环引用
json.dumps方法会检查Python对象中是否有循环引用,如果发现了循环引用,就会抛出异常.如下例所示:
>>>circular_obj={} >>>circular_obj['self']=circular_obj >>>circular_obj {'self':{...}} >>>json.dumps(circular_obj) Traceback(mostrecentcalllast): File"",line1,in File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py",line231,indumps return_default_encoder.encode(obj) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line199,inencode chunks=self.iterencode(o,_one_shot=True) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line257,initerencode return_iterencode(o,0) ValueError:Circularreferencedetected
如果不希望json.dumps方法检查循环引用,可以将参数check_circular设置为False.但如果此时Python对象中有循环引用,有可能发生递归嵌套过深的错误或者其他错误,这么做是比较危险的.如下例所示:
>>>json.dumps(circular_obj,check_circular=False) Traceback(mostrecentcalllast): File"",line1,in File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py",line238,indumps **kw).encode(obj) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line199,inencode chunks=self.iterencode(o,_one_shot=True) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line257,initerencode return_iterencode(o,0) RecursionError:maximumrecursiondepthexceededwhileencodingaJSONobject
3.4JSON字符串输出格式
json.dumps方法的indent参数可以用来控制JSON字符串的换行和缩进效果.
indent参数默认值为None.此时,JSON字符串不会有换行和缩进效果.如下示:
>>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}})) {"a":123,"b":{"x":321,"y":"ABC"}}
当indent为0或者负数时,JSON字符会包含换行:
>>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}},indent=-1)) { "a":123, "b":{ "x":321, "y":"ABC" } } >>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}},indent=0)) { "a":123, "b":{ "x":321, "y":"ABC" } }
而当indent为正整数时,除了换行,JSON还会以指定数量的空格为单位在对象层次间进行缩进:
>>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}},indent=2)) { "a":123, "b":{ "x":321, "y":"ABC" } }
indent还可以是str,此时,JSON会以str内容为单位进行缩进,比如制表符\t:
>>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}},indent='\t')) { "a":123, "b":{ "x":321, "y":"ABC" } }
json.dumps的另外一个参数separators可以用来设置输出的分隔符.这个参数的值应当是一个有两个元素的tuple.其第一个值为成员间的分隔符,第二个值为键值之间的分隔符.其默认值也会随上文中的indent参数影响.当indent为None时,separators的默认值为(',',':'),即分隔符后都有一个空格.当indent不为None时,其默认值则为(',',':'),即只有键值间分隔符后会有一个空格,而元素间分隔符则不带空格,因为此时会有换行.
separators参数的一种可能的使用场景是希望移除所有的非必要格式字符,以此来减小JSON字符串的大小.此时可以将separator设置为(',',';'),并不设置indent参数,或者将其显式设置为None:
>>>print(json.dumps({'a':123,'b':{'x':321,'y':'ABC'}},indent=None,separators=(',',':'))) {"a":123,"b":{"x":321,"y":"ABC"}}
3.5转换自定义Python对象
json.dumps的默认实现只能转换Dictionary类型的对象.如果想要转换自定义对象,需要使用default参数.这个参数接收一个函数,这个函数的参数是一个要转换的Python对象,返回值是能够表示这个Python对象的Dictionary对象.default函数会从对象引用树的顶层开始,逐层遍历整个对象引用树.因此,不用自己实现对象树的遍历逻辑,只需要处理当前层次的对象.如下例所示:
>>>classMyClass: ...def__init__(self,x,y): ...self.x=x ...self.y=y ... >>>defmy_default(o): ...ifisinstance(o,MyClass): ...print('%s.y:%s'%(type(o),o.y)) ...return{'x':o.x,'y':o.y} ...print(o) ...returno ... >>>obj=MyClass(x=MyClass(x=1,y=2),y=11) >>>json.dumps(obj,default=my_default).y:11 .y:2 '{"x":{"x":1,"y":2},"y":11}'
3.6非字符串类型键名
在Python中,只是可哈希(hashable)的对象和数据都可以做为Dictionary对象的键,而JSON规范中则只能使用字符串做为键名.所以在json.dumps的实现中,对这个规则进行了检查,不过键名允许的范围有所扩大,str,int,float,bool和None类型的数据都可以做为键名.不过当键名非str的情况时,键名会转换为对应的str值.如下例:
>>>json.dumps( ...{ ...'str':'str', ...123:123, ...321.54:321.54, ...True:True, ...False:False, ...None:None ...} ...) '{"str":"str","123":123,"321.54":321.54,"true":true,"false":false,"null":null}'
而当出现其他类型的键名时,默认出抛出异常:
>>>json.dumps({(1,2):123}) Traceback(mostrecentcalllast): File"",line1,in File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py",line231,indumps return_default_encoder.encode(obj) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line199,inencode chunks=self.iterencode(o,_one_shot=True) File"/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py",line257,initerencode return_iterencode(o,0) TypeError:keysmustbeastring
json.dumps的skipkeys参数可以改变这个行为.当将skipkeys设置为True时,遇到非法的键名类型,不会抛出异常,而是跳过这个键名:
>>>json.dumps({(1,2):123},skipkeys=True) '{}'
3.7生成JSON文件
当需要将生成的JSON数据保存到文件时,可以使用json.dump方法.这个方法比json.dumps多了一个参数fp,这个参数就是用来保存JSON数据的文件对象.比如,下例中的代码
>>>withopen('/tmp/data.json',mode='a')asjf: ...json.dump({'a':123},jf) ...
就会将JSON数据写入到/tmp/data.json文件里.代码执行完后,文件内容为
{"a":123} json.dump方法也可以接受其他类文件对象: >>>sio=io.StringIO() >>>json.dump({'a':123},sio) >>>sio.getvalue() '{"a":123}'
json.dump的其他参数和json.dumps的用法相同,这里不再赘述.
4JSON解码和编码类实现
json.loads,json.load,json.dumps和json.dump这四个方法是通过json.JSONDecoder和json.JSONEncoder这两个类来完成各自的任务的.所以也可以直接使用这两个类来完成前文描述的功能:
>>>json.JSONDecoder().decode('{"a":123}') {'a':123} >>>json.JSONEncoder().encode({'a':123}) '{"a":123}'
json.loads,json.load,json.dumps和json.dump这个四个方法的参数主要都是传递给了json.JSONDecoder和json.JSONEncoder的构造方法,所以使用这些方法可以满足绝大部分需求.当需要自定义json.JSONDecoder和json.JSONEncoder子类的时候,只需要将子类传递给cls参数.同时,这些方法都有**kw参数.当自定义实现类的构造函数需要标准参数列表之外的新参数时,这个参数就会将新参数传递给实现类的构造方法.
5相关资源
- JSON
- TheJavaScriptObjectNotation(JSON)DataInterchangeFormat-RFC4627
- json—JSONencoderanddecoder
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。