JSON (JavaScript Object Notation)是一種使用普遍的輕量數據格式. Python標準庫中的json
模塊提供了JSON數據的處理功能.html
Python中一種很是經常使用的基本數據結構就是字典(Dictionary). 它的典型結構以下:python
d = { 'a': 123, 'b': { 'x': ['A', 'B', 'C'] } }
而JSON的結構以下:json
{ "a": 123, "b": { "x": ["A", "B", "C"] } }
能夠看到, Dictionary和JSON很是接近, 而Python中的json
庫提供的主要功能, 也是二者之間的轉換.數組
json.loads
方法能夠將包含了一個JSON數據的str
, bytes
或者bytearray
對象, 轉化爲一個Python Dictionary. 它的完型接口簽名以下:數據結構
json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
json.loads
最基本的使用方式就是將一個包含JSON數據的str
傳遞給這個方法:函數
>>> json.loads('{"a": 123}') {'a': 123}
注意編碼
在Python中, str
值能夠放在一對單引號中, 也能夠放在一對雙引號中:code
>>> 'ABC' == "ABC" True
因此, 在定義Dictionary的str
類型的鍵和值的時候, 使用單引號或者雙引號都是合法和等價的:orm
>>> {"a": 'ABC'} == {'a': "ABC"} True
可是, 在JSON中, 字符串數據只能放在雙引號中, 於是json.loads
方法處理的字符串的JSON內容中, 字符串必須使用雙引號. 不然就會發生解碼錯誤:htm
>>> json.loads("{'a': 123}") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 354, in loads return _default_decoder.decode(s) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py", line 339, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/decoder.py", line 355, in raw_decode obj, end = self.scan_once(s, idx) json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
若是被處理的Python字符串是包含在雙引號中的, 那麼JSON中的雙引號就須要轉義:
>>> json.loads("{\"a\": 123}") {'a': 123}
bytes
和bytearray
數據對於內容是JSON數據的bytes
和bytearray
, json.loads
方法也能夠處理:
>>> json.loads('{"a": 123}'.encode('UTF-8')) {'a': 123} >>> json.loads(bytearray('{"a": 123}', 'UTF-8')) {'a': 123}
json.loads
的第二個參數是encoding
沒有實際做用.
因爲Python 3中str
類型老是使用UTF-8編碼, 因此s
參數爲str
類型時, json.loads
方法自動使用UTF-8編碼. 而且, str
不能以BOM字節開頭.
當s
參數爲bytes
或者bytearray
時, json.loads
方法會自動判斷爲UTF-8, UTF-16仍是UTF-32編碼. 默認也是將其按照UTF-8編碼轉化爲str
對象進行後續處理.
JSON能夠表示四種主類型數據
字符串 string
數字 number
布爾類 boolean
空值 null
以及兩結數據結構
對象 object
數組 array
默認實現中, JSON和Python之間的數據轉換對應關係以下表:
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
實際轉換狀況以下例:
>>> json.loads(""" ... { ... "obj": { ... "str": "ABC", ... "int": 123, ... "float": -321.89, ... "bool_true": true, ... "bool_false": false, ... "null": null, ... "array": [1, 2, 3] ... } ... }""") {'obj': {'str': 'ABC', 'int': 123, 'float': -321.89, 'bool_true': True, 'bool_false': False, 'null': None, 'array': [1, 2, 3]}}
對於JSON中數字number類型的數據, 有如下幾點須要注意:
JSON中的實數real number類型的精度不能超過Python中的float類型的精度範圍, 不然就有精度損失. 以下例:
>>> json.loads('3.141592653589793238462643383279') 3.141592653589793
JSON標準不包括非數字NaN, 正無窮Infinity和負無窮-Infinity, 可是json.loads
方法默認會將JSON字符串中的NaN
, Infinity
, -Infinity
轉化爲Python中的float('nan')
, float('inf')
和float('-inf')
. 注意, 這裏JSON中的NaN
, Infinity
, -Infinity
必須大小寫正確而且拼寫完整. 以下例
>>> json.loads('{"inf": Infinity, "nan": NaN, "ninf": -Infinity}') {'inf': inf, 'nan': nan, 'ninf': -inf}
json.loads
默認將JSON中的對象數據轉化爲Dictionary類型, object_hook
參數能夠用來改變構造出的對象.
object_hook
接受一個函數, 這個函數的輸入參數爲JSON中對象數據轉化出的Dictionary對象, 其返回值則爲自定義的對象. 以下例所示:
>>> class MyJSONObj: ... def __init__(self, x): ... self.x = x ... >>> def my_json_obj_hook(data): ... print('obj_hook data: %s' % data) ... return MyJSONObj(data['x']) ... >>> result = json.loads('{"x": 123}', object_hook=my_json_obj_hook) obj_hook data: {'x': 123} >>> type(result) <class '__main__.MyJSONObj'> >>> result.x 123
當JSON中的對象有嵌套時, json.loads
方法會按照深度優先的方式遍歷對象樹, 將各層的對象數據傳遞給object_hook
. 葉節點的JSON對象構造出的Python對象, 會做爲父節點的一個值, 傳遞給父節點的object_hook
方法. 以下例:
>>> class MyJSONObj: ... def __init__(self, x, y): ... self.x = x ... self.y = y ... >>> def my_json_obj_hook(data): ... print('obj_hook data: %s' % data) ... return MyJSONObj(**data) ... >>> result = json.loads('{"x": {"x": 11, "y": 12}, "y": {"x": 21, "y":22}}', object_hook=my_json_obj_hook) obj_hook data: {'x': 11, 'y': 12} obj_hook data: {'x': 21, 'y': 22} obj_hook data: {'x': <__main__.MyJSONObj object at 0x10417ef28>, 'y': <__main__.MyJSONObj object at 0x10417ed68>}
除了object_hook
參數之外, 還有一個object_pairs_hook
參數. 這個參數一樣能夠用來改變json.loads
方法構造出的Python對象的類型. 這個參數和object_hook
的不一樣, 在於傳入的方法所接收到的輸入數據不是一個Dictionary, 而是一個包含tuple
的list
. 每一個tuple
都有兩個元素, 第一個元素是JSON數據中的鍵, 第二個元素是這個鍵對應的值. 如JSON對象
{ "a": 123, "b": "ABC" }
對應的輸入數據是
[ ('a': 123), ('b', 'ABC') ]
當調用json.loads
方法時, 同時指定object_hook
和object_pairs_hook
, object_pairs_hook
會覆蓋object_hook
參數.
默認實現中, JSON中的實數被轉換爲Python的float
類型, 整數被轉換爲int
或者long
類型. 相似object_hook
, 咱們能夠經過parse_float
和parse_int
參數指定自定義的轉換邏輯. 這兩個方法的輸入參數爲表示JSON實數或者整數的字符串. 下例中, 咱們將實數轉換爲numpy.float64
, 將整數轉換爲numpy.int64
:
>>> def my_parse_float(f): ... print('%s(%s)' % (type(f), f)) ... return numpy.float64(f) ... >>> def my_parse_int(i): ... print('%s(%s)' % (type(i), i)) ... return numpy.int64(i) ... >>> result = json.loads('{"i": 123, "f": 321.45}', parse_float=my_parse_float, parse_int=my_parse_int) <type 'str'>(123) <type 'str'>(321.45) >>> type(result['i']) <type 'numpy.int64'> >>> type(result['f']) <type 'numpy.float64'>
NaN
, Infinity
和-Infinity
轉換類型因爲標準JSON數據不支持NaN
, Infinity
和-Infinity
, 因此parse_float
並不會接收到這幾個值. 當須要自定義這幾個值轉換的對象的時候, 就須要使用另一個接口parse_constant
. 好比下例中, 將這幾個值一樣轉換爲numpy.float64
類型:
>>> def my_parse_constant(data): ... print('%s(%s)' % (type(data), data)) ... return numpy.float64(data) ... >>> result = json.loads('{"inf": Infinity, "nan": NaN, "ninf": -Infinity}', parse_constant=my_parse_constant) <type 'str'>(Infinity) <type 'str'>(NaN) <type 'str'>(-Infinity) >>> result['inf'] inf >>> type(result['inf']) <type 'numpy.float64'>
根據JSON規範, 一個JSON數據中, 能夠只包含一個值, 而不是一個完整的對象. 這個值能夠是一個字符串, 一個數字, 布爾值, 空值, 或者一個數組. 除了這三種JSON規範中給出的類型, 還能夠是NaN
, Infinity
或者-Infinity
:
>>> json.loads('"hello"') 'hello' >>> json.loads('123') 123 >>> json.loads('123.34') 123.34 >>> json.loads('true') True >>> json.loads('false') False >>> print(json.loads('null')) None >>> json.loads('[1, 2, 3]') [1, 2, 3]
在同一層級JSON對象中, 不該當出現重複的鍵名, 不過JSON規範中沒有給出這種狀況的處理標準. 在json.loads
中, 當JSON數據中有重複鍵名, 則後面的鍵值會覆蓋前面的:
>>> json.loads('{"a": 123, "b": "ABC", "a": 321}') {'a': 321, 'b': 'ABC'}
當JSON數據是保存在一個文件中的時候, json.load
方法能夠用來從這個文件中讀取數據, 並轉換爲Python對象. json.load
方法的第一個參數就是指向JSON數據文件的文件類型對象.
好比/tmp/data.json
文件的內含以下:
{ "a": 123, "b": "ABC" }
可使用下例中的代碼來讀取並轉化文件中的JSON數據:
>>> with open('/tmp/data.json') as jf: ... json.load(jf) ... {u'a': 123, u'b': u'ABC'}
除了文件類型的對象, 只要是實現了read
方法的類文件對象, 均可以做爲fp
參數, 好比下例中的io.StringIO
:
>>> sio = io.StringIO('{"a": 123}') >>> json.load(sio) {'a': 123}
json.load
方法的其餘參數的意義和使用方法和上文中的json.loads
相同, 這裏再也不贅述.
json.dumps
方法能夠將Python對象轉換爲一個表示JONS數據的字符串. 它的完整接口簽名以下:
json.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)
它的第一個參數obj
即爲要轉換的數據對象.
>>> json.dumps({'a': 123, 'b': 'ABC'}) '{"a": 123, "b": "ABC"}'
json.dumps
的ensure_ascii
參數用來控制生成的JSON字符串的編碼. 其默認值爲True
, 此時, 全部的非ASCII碼字條都會轉義. 若是不但願自動進行轉義, 則會保持原有編碼, 限UTF-8. 以下例所示:
>>> json.dumps({'數字': 123, '字符': '一二三'}) '{"\\u6570\\u5b57": 123, "\\u5b57\\u7b26": "\\u4e00\\u4e8c\\u4e09"}' >>> json.dumps({'數字': 123, '字符': '一二三'}, ensure_ascii=False) '{"數字": 123, "字符": "一二三"}'
在默認實現中, json.dumps
能夠處理的Python對象, 及其全部的屬性值, 類型必須爲dict
, list
, tuple
, str
, float
或者int
. 這些類型與JSON的數據轉換關係以下表:
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int-&float-derived emuns | number |
True | true |
False | false |
None | null |
實際轉換狀況以下示例:
>>> json.dumps( ... { ... 'str': 'ABC', ... 'int': 123, ... 'float': 321.45, ... 'bool_true': True, ... 'bool_false': False, ... 'none': None, ... 'list': [1, 2, 3], ... 'tuple': [12, 34] ... } ... ) '{"str": "ABC", "int": 123, "float": 321.45, "bool_true": true, "bool_flase": false, "none": null, "list": [1, 2, 3], "tuple": [12, 34]}'
雖然JSON標準規範不支持NaN
, Infinity
和-Infinity
, 可是json.dumps
的默認實現會將float('nan')
, float('inf')
和float('-inf')
轉換爲常量NaN, Infinity, 和-Infinity. 以下例所示:
>>> json.dumps( ... { ... 'nan': float('nan'), ... 'inf': float('inf'), ... '-inf': float('-inf') ... } ... ) '{"nan": NaN, "inf": Infinity, "-inf": -Infinity}'
因爲這些常量可能會致使生成的JSON字符串不能被其餘的JSON實現處理, 爲了防止這種狀況出現, 能夠將json.dumps
的allow_nan
參數設置爲True
. 此時, 當處理的Python對象中出現這些值時, json.dumps
方法會拋出異常.
json.dumps
方法會檢查Python對象中是否有循環引用, 若是發現了循環引用, 就會拋出異常. 以下例所示:
>>> circular_obj = {} >>> circular_obj['self'] = circular_obj >>> circular_obj {'self': {...}} >>> json.dumps(circular_obj) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps return _default_encoder.encode(obj) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode chunks = self.iterencode(o, _one_shot=True) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode return _iterencode(o, 0) ValueError: Circular reference detected
若是不但願json.dumps
方法檢查循環引用, 能夠將參數check_circular
設置爲False
. 但若是此時Python對象中有循環引用, 有可能發生遞歸嵌套過深的錯誤或者其餘錯誤, 這麼作是比較危險的. 以下例所示:
>>> json.dumps(circular_obj, check_circular=False) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 238, in dumps **kw).encode(obj) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode chunks = self.iterencode(o, _one_shot=True) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode return _iterencode(o, 0) RecursionError: maximum recursion depth exceeded while encoding a JSON object
json.dumps
方法的indent
參數能夠用來控制JSON字符串的換行和縮進效果.
indent
參數默認值爲None
. 此時, JSON字符串不會有換行和縮進效果. 以下示:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}})) {"a": 123, "b": {"x": 321, "y": "ABC"}}
當indent
爲0或者負數時, JSON字符會包含換行:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=-1)) { "a": 123, "b": { "x": 321, "y": "ABC" } } >>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=0)) { "a": 123, "b": { "x": 321, "y": "ABC" } }
而當indent
爲正整數時, 除了換行, JSON還會以指定數量的空格爲單位在對象層次間進行縮進:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=2)) { "a": 123, "b": { "x": 321, "y": "ABC" } }
indent
還能夠是str
, 此時, JSON會以str
內容爲單位進行縮進, 好比製表符\t
:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent='\t')) { "a": 123, "b": { "x": 321, "y": "ABC" } }
json.dumps
的另一個參數separators
能夠用來設置輸出的分隔符. 這個參數的值應當是一個有兩個元素的tuple
. 其第一個值爲成員間的分隔符, 第二個值爲鍵值之間的分隔符. 其默認值也會隨上文中的indent
參數影響. 當indent
爲None
時, separators
的默認值爲(', ', ': ')
, 即分隔符後都有一個空格. 當indent
不爲None
時, 其默認值則爲(',', ':')
, 即只有鍵值間分隔符後會有一個空格, 而元素間分隔符則不帶空格, 由於此時會有換行.
separators
參數的一種可能的使用場景是但願移除全部的非必要格式字符, 以此來減少JSON字符串的大小. 此時能夠將separator
設置爲(',', ';')
, 並不設置indent
參數, 或者將其顯式設置爲None
:
>>> print(json.dumps({'a': 123, 'b': {'x': 321, 'y': 'ABC'}}, indent=None, separators=(',', ':'))) {"a":123,"b":{"x":321,"y":"ABC"}}
json.dumps
的默認實現只能轉換Dictionary類型的對象. 若是想要轉換自定義對象, 須要使用default
參數. 這個參數接收一個函數, 這個函數的參數是一個要轉換的Python對象, 返回值是可以表示這個Python對象的Dictionary對象. default
函數會從對象引用樹的頂層開始, 逐層遍歷整個對象引用樹. 所以, 不用本身實現對象樹的遍歷邏輯, 只須要處理當前層次的對象. 以下例所示:
>>> class MyClass: ... def __init__(self, x, y): ... self.x = x ... self.y = y ... >>> def my_default(o): ... if isinstance(o, MyClass): ... print('%s.y: %s' % (type(o), o.y)) ... return {'x': o.x, 'y': o.y} ... print(o) ... return o ... >>> obj = MyClass(x=MyClass(x=1, y=2), y=11) >>> json.dumps(obj, default=my_default) <class '__main__.MyClass'>.y: 11 <class '__main__.MyClass'>.y: 2 '{"x": {"x": 1, "y": 2}, "y": 11}'
在Python中, 只是可哈希(hashable)的對象和數據均可以作爲Dictionary對象的鍵, 而JSON規範中則只能使用字符串作爲鍵名. 因此在json.dumps
的實現中, 對這個規則進行了檢查, 不過鍵名容許的範圍有所擴大, str
, int
, float
, bool
和None
類型的數據均可以作爲鍵名. 不過當鍵名非str
的狀況時, 鍵名會轉換爲對應的str
值. 以下例:
>>> json.dumps( ... { ... 'str': 'str', ... 123: 123, ... 321.54: 321.54, ... True: True, ... False: False, ... None: None ... } ... ) '{"str": "str", "123": 123, "321.54": 321.54, "true": true, "false": false, "null": null}'
而當出現其餘類型的鍵名時, 默認出拋出異常:
>>> json.dumps({(1,2): 123}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/__init__.py", line 231, in dumps return _default_encoder.encode(obj) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 199, in encode chunks = self.iterencode(o, _one_shot=True) File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/json/encoder.py", line 257, in iterencode return _iterencode(o, 0) TypeError: keys must be a string
json.dumps
的skipkeys
參數能夠改變這個行爲. 當將skipkeys
設置爲True
時, 遇到非法的鍵名類型, 不會拋出異常, 而是跳過這個鍵名:
>>> json.dumps({(1,2): 123}, skipkeys=True) '{}'
當須要將生成的JSON數據保存到文件時, 可使用json.dump
方法. 這個方法比json.dumps
多了一個參數fp
, 這個參數就是用來保存JSON數據的文件對象. 好比, 下例中的代碼
>>> with open('/tmp/data.json', mode='a') as jf: ... json.dump({'a': 123}, jf) ...
就會將JSON數據寫入到/tmp/data.json
文件裏. 代碼執行完後, 文件內容爲
{"a": 123}
json.dump
方法也能夠接受其餘類文件對象:
>>> sio = io.StringIO() >>> json.dump({'a': 123}, sio) >>> sio.getvalue() '{"a": 123}'
json.dump
的其餘參數和json.dumps
的用法相同, 這裏再也不贅述.
json.loads
, json.load
, json.dumps
和json.dump
這四個方法是經過json.JSONDecoder
和json.JSONEncoder
這兩個類來完成各自的任務的. 因此也能夠直接使用這兩個類來完成前文描述的功能:
>>> json.JSONDecoder().decode('{"a": 123}') {'a': 123} >>> json.JSONEncoder().encode({'a': 123}) '{"a": 123}'
json.loads
, json.load
, json.dumps
和json.dump
這個四個方法的參數主要都是傳遞給了json.JSONDecoder
和json.JSONEncoder
的構造方法, 因此使用這些方法能夠知足絕大部分需求. 當須要自定義json.JSONDecoder
和json.JSONEncoder
子類的時候, 只須要將子類傳遞給cls
參數. 同時, 這些方法都有**kw
參數. 當自定義實現類的構造函數須要標準參數列表以外的新參數時, 這個參數就會將新參數傳遞給實現類的構造方法.