(一)什麼是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)。
這些都是常見的資料結構。事實上大部分現代電腦語言都以某種形式支援它們。這使得一種資料格式在同樣基於這些結構的程式設計語言之間交換成為可能。
(二)Python JSON模組
Python2.6開始加入了JSON模組,無需另外下載,Python的Json模組序列化與還原序列化的過程分別是 encoding和 decoding。encoding-把一個Python對象編碼轉換成Json字串;decoding-把Json格式字串解碼轉換成Python對象。要使用json模組必須先匯入:
1,單一資料型別的處理
Python JSON模組可以直接處理單一資料型別(string、unicode、int、float、list、tuple、dict)。 json.dumps()方法返回一個str對象,編碼過程中會存在從python原始類型向json類型的轉化過程,具體的轉化對照如下:
json.dumps方法提供了很多好用的參數可供選擇,比較常用的有sort_keys(對dict對象進行排序,我們知道預設dict是無序存放的)、separators,indent等參數,dumps方法的定義為:
json.dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True,cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False,**kw)
使用簡單的json.dumps方法對單一資料型別進行編碼,例如:
obj = [[1,2,3],123,123.123,'abc',{'key1':(1,2,3),'key2':(4,5,6)}] encodedjson = json.dumps(obj) print 'the original list:\n',obj print 'length of obj is:',len(repr(obj))print 'repr(obj),replace whiteblank with *:\n', repr(obj).replace(' ','*') print 'json encoded,replace whiteblank with *:\n',encodedjson.replace(' ','*')
輸出:(Python預設的item separator是‘, '(不是','),所以list無論是轉化成字串還是json格式,成員之間都是有空格隔開的)
the original list: [[1, 2, 3], 123, 123.123, 'abc', {'key2': (4, 5, 6), 'key1': (1, 2, 3)}] length of obj is: 72repr(obj),replace whiteblank with *: [[1,*2,*3],*123,*123.123,*'abc',*{'key2':*(4,*5,*6),*'key1':*(1,*2,*3)}] json encoded,replace whiteblank with *: [[1,*2,*3],*123,*123.123,*"abc",*{"key2":*[4,*5,*6],*"key1":*[1,*2,*3]}] <type 'list'>
我們接下來在對encodedjson進行decode,得到未經處理資料,需要使用的json.loads()函數。loads方法返回了原始的對象,但是仍然發生了一些資料類型的轉化,上例中‘abc'轉化為了unicode類型。需要注意的是,json字串中的字典類型的key必須要用雙引號“”json.loads()才能正常解析。從json到python的類型轉化對照如下:
decodejson = json.loads(encodedjson) print 'the type of decodeed obj from json:', type(decodejson) print 'the obj is:\n',decodejson print 'length of decoded obj is:',len(repr(decodejson))
輸出:
the type of decodeed obj from json: <type 'list'> the obj is: [[1, 2, 3], 123, 123.123, u'abc', {u'key2': [4, 5, 6], u'key1': [1, 2, 3]}] length of decoded obj is: 75 #比原obj多出了3個unicode編碼標示‘u'
sort_keys排序功能使得儲存的資料更加有利於觀察,也使得對json輸出的對象進行比較。下例中,data1和data2資料應該是一樣的,dict儲存的無序性造成兩者無法比較。
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 'sorted data1(d1):',d1 print 'unsorted data2(d2):',d2 print 'sorted data2(d3):',d3 print 'd1==d2?:',d1==d2 print 'd1==d3?:',d1==d3
輸出:
sorted data1(d1): {"a": 123, "b": 789, "c": 456} unsorted data2(d2): {"a": 123, "c": 456, "b": 789} sorted data2(d3): {"a": 123, "b": 789, "c": 456} d1==d2?: False d1==d3?: True
indent參數是縮排的意思,它可以使資料的儲存格式更優雅、可讀性更強,這是通過增加一些冗餘的空格進行填充的。但是在解碼(json.loads())時,空白填充會被刪除。
data = {'b':789,'c':456,'a':123} d1 = json.dumps(data,sort_keys=True,indent=4) print 'data len is:',len(repr(data)) print '4 indented data:\n',d1 d2 = json.loads(d1) print 'decoded DATA:', repr(d2) print 'len of decoded DATA:',len(repr(d2))
輸出:(可見loads時會將dumps時增加的intent 填充空格去除)
data len is: 30 4 indented data: { "a": 123, "b": 789, "c": 456 } decoded DATA: {u'a': 123, u'c': 456, u'b': 789} len of decoded DATA: 33
json主要是作為一種資料通訊的格式存在的,無用的空格會浪費通訊頻寬,適當時候也要對資料進行壓縮。separator參數可以起到這樣的作用,該參數傳遞是一個元組,包含分割對象的字串,其實質就是將Python預設的(‘, ',': ')分隔字元替換成(',',':')。
data = {'b':789,'c':456,'a':123} 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異常產生,如果將skipkeys設為True則會優雅的濾除非法keys。
data = {'b':789,'c':456,(1,2):123} print'original data:',repr(data) print 'json encoded',json.dumps(data,skipkeys=True)
輸出:
original data: {(1, 2): 123, 'c': 456, 'b': 789} json encoded {"c": 456, "b": 789}
2,JSON處理自訂資料類型
json模組不僅可以處理普通的python內建類型,也可以處理我們自訂的資料類型,而往往處理自訂的對象是很常用的。
如果直接通過json.dumps方法對Person的執行個體進行處理的話,會報錯,因為json無法支援這樣的自動轉化。通過上面所提到的json和 python的類型轉化對照表,可以發現,object類型是和dict相關聯的,所以我們需要把我們自訂的類型轉化為dict,然後再進行處理。這裡,有兩種方法可以使用。
方法一:自己寫轉化函數
自訂object類型和dict類型進行轉化:encode-定義函數 object2dict()將對象模組名、類名以及__dict__儲存在一個字典並返回;decode-定義dict2object()解析出模組名、類名、參數,建立新的對象並返回。在json.dumps()中通過default參數指定轉化過程中調用的函數;json.loads()則通過 object_hook指定轉化函數。
方法二:繼承JSONEncoder和JSONDecoder類,覆寫相關方法
JSONEncoder類負責編碼,主要是通過其default函數進行轉化,我們可以重載該方法。對於JSONDecoder,亦然。
#handling private data type #define class 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) #define transfer functions def object2dict(obj): #convert object to a dict d = {'__class__':obj.__class__.__name__, '__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) print 'the module is:', module class_ = getattr(module,class_name) args = dict((key.encode('ascii'), value) for key, value in d.items()) #get args print 'the atrribute:', repr(args) inst = class_(**args) #create new instance else: inst = d return inst #recreate the default method class LocalEncoder(json.JSONEncoder): def default(self,obj): #convert object to a dict d = {'__class__':obj.__class__.__name__, '__module__':obj.__module__} d.update(obj.__dict__) return d class LocalDecoder(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 #test function if __name__ == '__main__': p = Person('Aidan',22) print p #json.dumps(p)#error will be throwed d = object2dict(p) print 'method-json encode:', d o = dict2object(d) print 'the decoded obj type: %s, obj:%s' % (type(o),repr(o)) dump = json.dumps(p,default=object2dict) print 'dumps(default = object2dict):',dump load = json.loads(dump,object_hook = dict2object) print 'loads(object_hook = dict2object):',load d = LocalEncoder().encode(p) o = LocalDecoder().decode(d) print 'recereated encode method: ',d print 'recereated decode method: ',type(o),o
輸出:
Person Object name : Aidan , age : 22 method-json encode: {'age': 22, '__module__': '__main__', '__class__': 'Person', 'name': 'Aidan'} the module is: <module '__main__' from 'D:/Project/Python/study_json'> the atrribute: {'age': 22, 'name': 'Aidan'} the decoded obj type: <class '__main__.Person'>, obj:Person Object name : Aidan , age : 22 dumps(default = object2dict): {"age": 22, "__module__": "__main__", "__class__": "Person", "name": "Aidan"} the module is: <module '__main__' from 'D:/Project/Python/study_json'> the atrribute: {'age': 22, 'name': u'Aidan'} loads(object_hook = dict2object): Person Object name : Aidan , age : 22 recereated encode method: {"age": 22, "__module__": "__main__", "__class__": "Person", "name": "Aidan"} recereated decode method: <class '__main__.Person'> Person Object name : Aidan , age : 22