Python中的JSON模組
(一)什麼是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模組必須先匯入:
import 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: 72
repr(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