什麼是json: html
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。易於人閱讀和編寫。同時也易於機器解析和生成。它基於JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一個子集。JSON採用徹底獨立於語言的文本格式,可是也使用了相似於C語言家族的習慣(包括C, C++, C#, Java, JavaScript, Perl, Python等)。這些特性使JSON成爲理想的數據交換語言。 python
JSON建構於兩種結構: 編程
「名稱/值」對的集合(A collection of name/value pairs)。不一樣的語言中,它被理解爲對象(object),紀錄(record),結構(struct),字典(dictionary),哈希表 (hash table),有鍵列表(keyed list),或者關聯數組 (associative array)。
值的有序列表(An ordered list of values)。在大部分語言中,它被理解爲數組(array)。
這些都是常見的數據結構。事實上大部分現代計算機語言都以某種形式支持它們。這使得一種數據格式在一樣基於這些結構的編程語言之間交換成爲可能。 json
jso官方說明參見:http://json.org/ api
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]}] ide
經過輸出的結果能夠看出,簡單類型經過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, valuein 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, valuein 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類方法,稍微有點不一樣,可是改寫起來也不是很麻煩。看代碼應該就比較清楚了。