#參考資料:《The Python Standard Library by Example》
12.9 json 模組
JSON就是JavaScript Object Notation,這個模組完成了python對象和JSON字串的互相轉換。適用於Python的版本2.6和更高版本。
json模組提供了一個類似pickle的API,轉換記憶體中的Python對象為JavaScript對象符號(JSON)序列。JSON具有有許多語言的實現(尤其是JavaScript)。在AJAX應用中,廣泛的使用在Web伺服器和用戶端之間,
12.9.1 編碼解碼單一資料型別
JSON編碼識別python的如下類型:(string, unicode, int,float, list, tuple, and dict),比如:
import json
data = [ { 'a':'A', 'b':(2, 4), 'c':3.0 } ]
print 'DATA:', repr(data)
data_string = json.dumps(data)
print 'JSON:', data_string
執行結果:
$ python json_simple_types.py
DATA: [{'a': 'A', 'c': 3.0, 'b': (2, 4)}]
JSON: [{"a": "A", "c": 3.0, "b": [2, 4]}]
可以json的表示和python的repr差不多。這裡還要注意一點,字典中,把可變對象往後靠,是不是有什麼規則?
下例進行編碼再解碼:
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
print ’DATA :’, data
data_string = json.dumps(data)
print ’ENCODED:’, data_string
decoded = json.loads(data_string)
print ’DECODED:’, decoded
print ’ORIGINAL:’, type(data[0][’b’])
print ’DECODED :’, type(decoded[0][’b’])
執行結果:
$ python json_simple_types_decode.py
DATA : [{’a’: ’A’, ’c’: 3.0, ’b’: (2, 4)}]
ENCODED: [{"a": "A", "c": 3.0, "b": [2, 4]}]
DECODED: [{’a’: ’A’, ’c’: 3.0, ’b’: [2, 4]}]
ORIGINAL: <type ’tuple’>
DECODED : <type ’list’>
這裡元組:(2, 4), 編碼再解碼之後,成了列表
12.9.2 編碼解碼單一資料型別
JSON跟pickle比的另一個好處是結果的可讀性比較好。pickle函數接受多個參數,以使輸出甚至更好。下例實現了排序功能:
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
print ’DATA:’, repr(data)
unsorted = json.dumps(data)
print ’JSON:’, json.dumps(data)
print ’SORT:’, json.dumps(data, sort_keys=True)
first = json.dumps(data, sort_keys=True)
second = json.dumps(data, sort_keys=True)
print ’UNSORTED MATCH:’, unsorted == first
print ’SORTED MATCH :’, first == second
執行結果:
$ python json_sort_keys.py
DATA: [{’a’: ’A’, ’c’: 3.0, ’b’: (2, 4)}]
JSON: [{"a": "A", "c": 3.0, "b": [2, 4]}]
SORT: [{"a": "A", "b": [2, 4], "c": 3.0}]
UNSORTED MATCH: False
SORTED MATCH : True
對於多層嵌套的資料結構,還可以使用縮排:
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
print ’DATA:’, repr(data)
print ’NORMAL:’, json.dumps(data, sort_keys=True)
print ’INDENT:’, json.dumps(data, sort_keys=True, indent=2)
執行結果:
$ python json_indent.py
DATA: [{’a’: ’A’, ’c’: 3.0, ’b’: (2, 4)}]
NORMAL: [{"a": "A", "b": [2, 4], "c": 3.0}]
INDENT: [
{
"a": "A",
"b": [
2,
4
],
"c": 3.0
}
]
當縮排不是負數的時候,和pprint的效果差不多。
像這樣詳細的輸出,因此它不適合在產品環境中使用。可以調整分離編碼輸出資料使它甚至超過了預設的緊湊。
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
print ’DATA:’, repr(data)
print ’repr(data) :’, len(repr(data))
plain_dump = json.dumps(data)
print ’dumps(data) :’, len(plain_dump)
small_indent = json.dumps(data, indent=2)
print ’dumps(data, indent=2) :’, len(small_indent)
with_separators = json.dumps(data, separators=(’,’,’:’))
print ’dumps(data, separators):’, len(with_separators)
執行結果:
$ python json_compact_encoding.py
DATA: [{’a’: ’A’, ’c’: 3.0, ’b’: (2, 4)}]
repr(data) : 35
dumps(data) : 35
dumps(data, indent=2) : 76
dumps(data, separators): 29
12.9.3 編碼字典
JSON字典的索引值是字串。試圖編碼非字串作為索引值會產生一個例外,是TypeError 或者ValueError,取決於載入的模組是純Python版本載入還是C的加速版本,可以讓json忽略這些非字串的索引值。
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0, (’d’,):’D tuple’ } ]
print ’First attempt’
try:
print json.dumps(data)
except (TypeError, ValueError), err:
print ’ERROR:’, err
print
print ’Second attempt’
print json.dumps(data, skipkeys=True)
執行結果:
import json
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0, (’d’,):’D tuple’ } ]
print ’First attempt’
try:
print json.dumps(data)
except (TypeError, ValueError), err:
print ’ERROR:’, err
print
print ’Second attempt’
print json.dumps(data, skipkeys=True)
12.9.4 自訂類型
到目前為止,所有的例子使用了Python的內建類型,因為這些都是由JSON本身支援的。如果需要編碼自訂類,有兩種方法。比如有如下對象:
class MyObj(object):
def __init__(self, s):
self.s = s
def __repr__(self):
return ’<MyObj(%s)>’ % self.s
編碼MyObj執行個體的最簡單的方法是定義一個函數來轉換未知類型為已知類型。它不需要做編碼,因此它應該只是轉換一個對象到另一個。
import json
import json_myobj
obj = json_myobj.MyObj(’instance value goes here’)
print ’First attempt’
try:
print json.dumps(obj)
except TypeError, err:
print ’ERROR:’, err
def convert_to_builtin_type(obj):
print ’default(’, repr(obj), ’)’
# Convert objects to a dictionary of their representation
d = { ’__class__’:obj.__class__.__name__,
’__module__’:obj.__module__,
}
d.update(obj.__dict__)
return d
print
print ’With default’
print json.dumps(obj, default=convert_to_builtin_type)
執行結果:
$ python json_dump_default.py
First attempt
ERROR: <MyObj(instance value goes here)> is not JSON serializable
With default
default( <MyObj(instance value goes here)> )
{"s": "instance value goes here", "__module__": "json_myobj",
"__class__": "MyObj"}
要解碼的結果並建立一個MyObj中()執行個體,使用object_hook參數到loads()綁上給解碼器,這樣的類可以從模組匯入並用來建立執行個體。
每個從輸入資料流中解碼的字典調用object_hook,把字典轉換為其他資料類型。Hook函數程式應該接收的對象而不是字典。
import json
def dict_to_object(d):
if '__class__' in d:
class_name = d.pop('__class__')
module_name = d.pop('__module__')
module = __import__(module_name)
print 'MODULE:', module.__name__
class_ = getattr(module, class_name)
print 'CLASS:', class_
args = dict( (key.encode('ascii'), value)
for key, value in d.items())
print 'INSTANCE ARGS:', args
inst = class_(**args)
else:
inst = d
return inst
encoded_object = '''
[{"s": "instance value goes here",
"__module__": "json_myobj", "__class__": "MyObj"}]
'''
myobj_instance = json.loads(encoded_object,
object_hook=dict_to_object)
print myobj_instance
運行結果:
$ python json_load_object_hook.py
MODULE: json_myobj
CLASS: <class ’json_myobj.MyObj’>
INSTANCE ARGS: {’s’: ’instance value goes here’}
[<MyObj(instance value goes here)>]
由於JSON字串值轉換為Unicode對象,他們需要重新編碼
為ASCII字串,才可以作為類構造的關鍵字參數。
12.9.5 編碼和解碼類
json模組提供了編碼和解碼類。使用這些類可訪問額外的API來定製自己的行為。
JSONEncoder使用了一個可迭代介面編碼資料的“chunks”,從而在不需要在記憶體表示整個資料,更加容易寫檔案或網路socket。
import json
encoder = json.JSONEncoder()
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
for part in encoder.iterencode(data):
print ’PART:’, part
執行結果:
$ python json_encoder_iterable.py
PART: [
PART: {
PART: "a"
PART: :
PART: "A"
PART: ,
PART: "c"
PART: :
PART: 3.0
PART: ,
PART: "b"
PART: :
PART: [2
PART: , 4
PART: ]
PART: }
PART: ]
encode()方法基本上等同於’ ’.join(encoder.iterencode()),不過事先多了一些錯誤檢查。要編碼的任意對象,使用前面類似convert_to_builtin_type的方法重載default()。
import json
import json_myobj
class MyEncoder(json.JSONEncoder):
def default(self, obj):
print 'default(', repr(obj), ')'
# Convert objects to a dictionary of their representation
d = { '__class__':obj.__class__.__name__,
'__module__':obj.__module__,
}
d.update(obj.__dict__)
return d
obj = json_myobj.MyObj('internal data')
print obj
print MyEncoder().encode(obj)
執行結果:
$ python json_encoder_default.py
<MyObj(internal data)>
default( <MyObj(internal data)> )
{"s": "internal data", "__module__": "json_myobj", "__class__":
"MyObj"}
解碼文本,然後轉換對象到字典,比之前的實現要稍微多一點步驟
import json
class MyDecoder(json.JSONDecoder):
def __init__(self):
json.JSONDecoder.__init__(self,
object_hook=self.dict_to_object)
def dict_to_object(self, d):
if ’__class__’ in d:
class_name = d.pop(’__class__’)
module_name = d.pop(’__module__’)
module = __import__(module_name)
print ’MODULE:’, module.__name__
class_ = getattr(module, class_name)
print ’CLASS:’, class_
args = dict( (key.encode(’ascii’), value)
for key, value in d.items())
print ’INSTANCE ARGS:’, args
inst = class_(**args)
else:
inst = d
return inst
encoded_object = ’’’
[{"s": "instance value goes here",
"__module__": "json_myobj", "__class__": "MyObj"}]
’’’
myobj_instance = MyDecoder().decode(encoded_object)
print myobj_instance。
執行結果:
$ python json_decoder_object_hook.py
MODULE: json_myobj
CLASS: <class ’json_myobj.MyObj’>
INSTANCE ARGS: {’s’: ’instance value goes here’}
[<MyObj(instance value goes here)>]
12.9.6 流和檔案
函數load() 和dump()用於讀寫檔案類似的對象。
import json
from StringIO import StringIO
data = [ { ’a’:’A’, ’b’:(2, 4), ’c’:3.0 } ]
f = StringIO()
json.dump(data, f)
print f.getvalue()
執行結果:
$ python json_dump_file.py
[{"a": "A", "c": 3.0, "b": [2, 4]}]
雖然不最佳化為一次唯讀取的部分資料,load()提供了從流輸入產生對象邏輯。
import json
from StringIO import StringIO
f = StringIO(’[{"a": "A", "c": 3.0, "b": [2, 4]}]’)
print json.load(f)
執行結果:
$ python json_load_file.py
[{’a’: ’A’, ’c’: 3.0, ’b’: [2, 4]}]
12.9.7 混合資料流
JSONDecoder包括raw_decode(),這個方法可以解碼包含更多資料的資料結構
,比如帶後續文本的JSON資料。傳回值是解碼後的輸入資料和索引。
(obj, end, remaining)
encoded_object = ’[{"a": "A", "c": 3.0, "b": [2, 4]}]’
extra_text = ’This text is not JSON.’
print ’JSON first:’
data = ’ ’.join([encoded_object, extra_text])
obj, end, remaining = get_decoded_and_remainder(data)
print ’Object :’, obj
print ’End of parsed input :’, end
print ’Remaining text :’, repr(remaining)
print
print ’JSON embedded:’
try:
data = ’ ’.join([extra_text, encoded_object, extra_text])
obj, end, remaining = get_decoded_and_remainder(data)
except ValueError, err:
print ’ERROR:’, err
執行結果:
$ python json_mixed_data.py
JSON first:
Object : [{’a’: ’A’, ’c’: 3.0, ’b’: [2, 4]}]
End of parsed input : 35
Remaining text : ’ This text is not JSON.’
JSON embedded:
ERROR: No JSON object could be decoded
注意,對象必須在輸入的開始
其他參考資料:
json (http://docs.python.org/library/json.html)
JavaScript Object Notation (http://json.org/)
simplejson (http://code.google.com/p/simplejson/)
simplejson (http://code.google.com/p/simplejson/)