什么是json:
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
JSON建构于两种结构:
“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。
jso官方说明参见:http://json.org/
Python操作json的标准api库参考:http://docs.python.org/library/json.html
对简单数据类型的encoding 和 decoding:
使用简单的json.dumps方法对简单数据类型进行编码,例如:
1
2
3
4
5
6
|
import json obj = [[ 1 , 2 , 3 ], 123 , 123.123 , ‘abc‘ ,{ ‘key1‘ :( 1 , 2 , 3 ), ‘key2‘ :( 4 , 5 , 6 )}] encodedjson = json.dumps(obj) print repr (obj) print encodedjson |
输出:
[[1, 2, 3], 123, 123.123, ‘abc‘, {‘key2‘: (4, 5, 6), ‘key1‘: (1, 2, 3)}]
[[1, 2, 3], 123, 123.123, "abc", {"key2": [4, 5, 6], "key1": [1, 2, 3]}]
通过输出的结果可以看出,简单类型通过encode之后跟其原始的repr()输出结果非常相似,但是有些数据类型进行了改变,例如上例中的元组则转换为了列表。在json的编码过程中,会存在从python原始类型向json类型的转化过程,具体的转化对照如下:
json.dumps()方法返回了一个str对象encodedjson,我们接下来在对encodedjson进行decode,得到原始数据,需要使用的json.loads()函数:
1
2
3
4
|
decodejson = json.loads(encodedjson) print type (decodejson) print decodejson[ 4 ][ ‘key1‘ ] print decodejson |
输出:
<type ‘list‘>
[1, 2, 3]
[[1, 2, 3], 123, 123.123, u‘abc‘, {u‘key2‘: [4, 5, 6], u‘key1‘: [1, 2, 3]}]
loads方法返回了原始的对象,但是仍然发生了一些数据类型的转化。比如,上例中‘abc’转化为了unicode类型。从json到python的类型转化对照如下:
json.dumps方法提供了很多好用的参数可供选择,比较常用的有sort_keys(对dict对象进行排序,我们知道默认dict是无序存放的),separators,indent等参数。
排序功能使得存储的数据更加有利于观察,也使得对json输出的对象进行比较,例如:
1
2
3
4
5
6
7
8
9
10
|
data1 = { ‘b‘ : 789 , ‘c‘ : 456 , ‘a‘ : 123 } data2 = { ‘a‘ : 123 , ‘b‘ : 789 , ‘c‘ : 456 } d1 = json.dumps(data1,sort_keys = True ) d2 = json.dumps(data2) d3 = json.dumps(data2,sort_keys = True ) print d1 print d2 print d3 print d1 = = d2 print d1 = = d3 |
输出:
{"a": 123, "b": 789, "c": 456}
{"a": 123, "c": 456, "b": 789}
{"a": 123, "b": 789, "c": 456}
False
True
上例中,本来data1和data2数据应该是一样的,但是由于dict存储的无序特性,造成两者无法比较。因此两者可以通过排序后的结果进行存储就避免了数据比较不一致的情况发生,但是排序后再进行存储,系统必定要多做一些事情,也一定会因此造成一定的性能消耗,所以适当排序是很重要的。
indent参数是缩进的意思,它可以使得数据存储的格式变得更加优雅。
1
2
3
|
data1 = { ‘b‘ : 789 , ‘c‘ : 456 , ‘a‘ : 123 } d1 = json.dumps(data1,sort_keys = True ,indent = 4 ) print d1 |
输出:
{
"a": 123,
"b": 789,
"c": 456
}
输出的数据被格式化之后,变得可读性更强,但是却是通过增加一些冗余的空白格来进行填充的。json主要是作为一种数据通信的格式存在的,而网络通信是很在乎数据的大小的,无用的空格会占据很多通信带宽,所以适当时候也要对数据进行压缩。separator参数可以起到这样的作用,该参数传递是一个元组,包含分割对象的字符串。
1
2
3
4
5
|
print ‘DATA:‘ , repr (data) print ‘repr(data) :‘ , len ( repr (data)) print ‘dumps(data) :‘ , len (json.dumps(data)) print ‘dumps(data, indent=2) :‘ , len (json.dumps(data, indent = 4 )) print ‘dumps(data, separators):‘ , len (json.dumps(data, separators = ( ‘,‘ , ‘:‘ ))) |
输出:
DATA: {‘a‘: 123, ‘c‘: 456, ‘b‘: 789}
repr(data) : 30
dumps(data) : 30
dumps(data, indent=2) : 46
dumps(data, separators): 25
通过移除多余的空白符,达到了压缩数据的目的,而且效果还是比较明显的。
另一个比较有用的dumps参数是skipkeys,默认为False。 dumps方法存储dict对象时,key必须是str类型,如果出现了其他类型的话,那么会产生TypeError异常,如果开启该参数,设为True的话,则会比较优雅的过度。
1
2
|
data = { ‘b‘ : 789 , ‘c‘ : 456 ,( 1 , 2 ): 123 } print json.dumps(data,skipkeys = True ) |
输出:
{"c": 456, "b": 789}
处理自己的数据类型
json模块不仅可以处理普通的python内置类型,也可以处理我们自定义的数据类型,而往往处理自定义的对象是很常用的。
首先,我们定义一个类Person。
1
2
3
4
5
6
7
8
9
|
class Person( object ): def __init__( self ,name,age): self .name = name self .age = age def __repr__( self ): return ‘Person Object name : %s , age : %d‘ % ( self .name, self .age) if __name__ = = ‘__main__‘ : p = Person( ‘Peter‘ , 22 ) print p |
如果直接通过json.dumps方法对Person的实例进行处理的话,会报错,因为json无法支持这样的自动转化。通过上面所提到的json和python的类型转化对照表,可以发现,object类型是和dict相关联的,所以我们需要把我们自定义的类型转化为dict,然后再进行处理。这里,有两种方法可以使用。
方法一:自己写转化函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
‘‘‘ Created on 2011-12-14 @author: Peter ‘‘‘ import Person import json p = Person.Person( ‘Peter‘ , 22 ) def object2dict(obj): #convert object to a dict d = {} d[ ‘__class__‘ ] = obj.__class__.__name__ d[ ‘__module__‘ ] = obj.__module__ d.update(obj.__dict__) return d def dict2object(d): #convert dict to object if ‘__class__‘ in d: class_name = d.pop( ‘__class__‘ ) module_name = d.pop( ‘__module__‘ ) module = __import__ (module_name) class_ = getattr (module,class_name) args = dict ((key.encode( ‘ascii‘ ), value) for key, value in d.items()) #get args inst = class_ ( * * args) #create new instance else : inst = d return inst d = object2dict(p) print d #{‘age‘: 22, ‘__module__‘: ‘Person‘, ‘__class__‘: ‘Person‘, ‘name‘: ‘Peter‘} o = dict2object(d) print type (o),o #<class ‘Person.Person‘> Person Object name : Peter , age : 22 dump = json.dumps(p,default = object2dict) print dump #{"age": 22, "__module__": "Person", "__class__": "Person", "name": "Peter"} load = json.loads(dump,object_hook = dict2object) print load #Person Object name : Peter , age : 22 |
上面代码已经写的很清楚了,实质就是自定义object类型和dict类型进行转化。object2dict函数将对象模块名、类名以及__dict__存储在dict对象里,并返回。dict2object函数则是反解出模块名、类名、参数,创建新的对象并返回。在json.dumps 方法中增加default参数,该参数表示在转化过程中调用指定的函数,同样在decode过程中json.loads方法增加object_hook,指定转化函数。
方法二:继承JSONEncoder和JSONDecoder类,覆写相关方法
JSONEncoder类负责编码,主要是通过其default函数进行转化,我们可以override该方法。同理对于JSONDecoder。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
‘‘‘ Created on 2011-12-14 @author: Peter ‘‘‘ import Person import json p = Person.Person( ‘Peter‘ , 22 ) class MyEncoder(json.JSONEncoder): def default( self ,obj): #convert object to a dict d = {} d[ ‘__class__‘ ] = obj.__class__.__name__ d[ ‘__module__‘ ] = obj.__module__ d.update(obj.__dict__) return d class MyDecoder(json.JSONDecoder): def __init__( self ): json.JSONDecoder.__init__( self ,object_hook = self .dict2object) def dict2object( self ,d): #convert dict to object if ‘__class__‘ in d: class_name = d.pop( ‘__class__‘ ) module_name = d.pop( ‘__module__‘ ) module = __import__ (module_name) class_ = getattr (module,class_name) args = dict ((key.encode( ‘ascii‘ ), value) for key, value in d.items()) #get args inst = class_ ( * * args) #create new instance else : inst = d return inst d = MyEncoder().encode(p) o = MyDecoder().decode(d) print d print type (o), o |
对于JSONDecoder类方法,稍微有点不同,但是改写起来也不是很麻烦。看代码应该就比较清楚了。
--------------------------------------------------------------------------------
JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。JSON的数据格式其实就是python里面的字典格式,里面可以包含方括号括起来的数组,也就是python里面的列表。
在python中,有专门处理json格式的模块—— json 和 picle模块
Json 模块提供了四个方法: dumps、dump、loads、load
def 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): # Serialize ``obj`` to a JSON formatted ``str``. # 序列号 “obj” 数据类型 转换为 JSON格式的字符串
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object). 我理解为两个动作,一个动作是将”obj“转换为JSON格式的字符串,还有一个动作是将字符串写入到文件中,也就是说文件描述符fp是必须要的参数 """
示例代码:
>>> import json >>> json.dumps([]) # dumps可以格式化所有的基本数据类型为字符串 ‘[]‘ >>> json.dumps(1) # 数字 ‘1‘ >>> json.dumps(‘1‘) # 字符串 ‘"1"‘ >>> dict = {"name":"Tom", "age":23} >>> json.dumps(dict) # 字典 ‘{"name": "Tom", "age": 23}‘
a = {"name":"Tom", "age":23} with open("test.json", "w", encoding=‘utf-8‘) as f: # indent 超级好用,格式化保存字典,默认为None,小于0为零个空格 f.write(json.dumps(a, indent=4)) # json.dump(a,f,indent=4) # 和上面的效果一样
保存的文件效果:
二. loads 和 load
loads和load 反序列化方法
查看源码:
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): """Deserialize ``s`` (a ``str`` instance containing a JSON document) to a Python object. 将包含str类型的JSON文档反序列化为一个python对象"""
def load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw): """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object. 将一个包含JSON格式数据的可读文件饭序列化为一个python对象"""
实例:
>>> json.loads(‘{"name":"Tom", "age":23}‘) {‘age‘: 23, ‘name‘: ‘Tom‘}
import json with open("test.json", "r", encoding=‘utf-8‘) as f: aa = json.loads(f.read()) f.seek(0) bb = json.load(f) # 与 json.loads(f.read()) print(aa) print(bb) # 输出: {‘name‘: ‘Tom‘, ‘age‘: 23} {‘name‘: ‘Tom‘, ‘age‘: 23}
三. json 和 picle 模块
json模块和picle模块都有 dumps、dump、loads、load四种方法,而且用法一样。
不用的是json模块序列化出来的是通用格式,其它编程语言都认识,就是普通的字符串,
而picle模块序列化出来的只有python可以认识,其他编程语言不认识的,表现为乱码
不过picle可以序列化函数,但是其他文件想用该函数,在该文件中需要有该文件的定义(定义和参数必须相同,内容可以不同)
四. python对象(obj) 与json对象的对应关系
+-------------------+---------------+ | Python | JSON | +===================+===============+ | dict | object | +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ | str | string | +-------------------+---------------+ | int, float | number | +-------------------+---------------+ | True | true | +-------------------+---------------+ | False | false | +-------------------+---------------+ | None | null | +-------------------+---------------+
五. 总结
1. json序列化方法:
dumps:无文件操作 dump:序列化+写入文件
2. json反序列化方法:
loads:无文件操作 load: 读文件+反序列化
3. json模块序列化的数据 更通用
picle模块序列化的数据 仅python可用,但功能强大,可以序列号函数
4. json模块可以序列化和反序列化的 数据类型 见 python对象(obj) 与json对象的对应关系表
5. 格式化写入文件利用 indent = 4
---------------------------------------------------------------------
json.dumps 与 json.loads 实例
以下实例演示了 Python 数据结构转换为JSON:
#!/usr/bin/python3
import json
# Python 字典类型转换为 JSON 对象
data = {
‘no‘ : 1,
‘name‘ : ‘Runoob‘,
‘url‘ : ‘http://www.runoob.com‘
}
json_str = json.dumps(data)
print ("Python 原始数据:", repr(data))
print ("JSON 对象:", json_str)
执行以上代码输出结果为:
Python 原始数据: {‘url‘: ‘http://www.runoob.com‘, ‘no‘: 1, ‘name‘: ‘Runoob‘}
JSON 对象: {"url": "http://www.runoob.com", "no": 1, "name": "Runoob"}
通过输出的结果可以看出,简单类型通过编码后跟其原始的repr()输出结果非常相似。
接着以上实例,我们可以将一个JSON编码的字符串转换回一个Python数据结构:
#!/usr/bin/python3
import json
# Python 字典类型转换为 JSON 对象
data1 = {
‘no‘ : 1,
‘name‘ : ‘Runoob‘,
‘url‘ : ‘http://www.runoob.com‘
}
json_str = json.dumps(data1)
print ("Python 原始数据:", repr(data1))
print ("JSON 对象:", json_str)
# 将 JSON 对象转换为 Python 字典
data2 = json.loads(json_str)
print ("data2[‘name‘]: ", data2[‘name‘])
print ("data2[‘url‘]: ", data2[‘url‘])
执行以上代码输出结果为:
Python 原始数据: {‘name‘: ‘Runoob‘, ‘no‘: 1, ‘url‘: ‘http://www.runoob.com‘}
JSON 对象: {"name": "Runoob", "no": 1, "url": "http://www.runoob.com"}
data2[‘name‘]: Runoob
data2[‘url‘]: http://www.runoob.com
如果你要处理的是文件而不是字符串,你可以使用 json.dump() 和 json.load() 来编码和解码JSON数据。例如:
# 写入 JSON 数据
with open(‘data.json‘, ‘w‘) as f:
json.dump(data, f)
# 读取数据
with open(‘data.json‘, ‘r‘) as f:
data = json.load(f)