目錄
引言
你一定聽說過 JSON 吧。JSON 是當前最常用的資料傳輸格式之一,純文字,容易使用,方便閱讀,最重要的是在多個場合都被大量被使用。
既然 JSON 這麼好,那就讓我們繼續探險,去掌握 python 中對 JSON 的常用操作吧, okay, let`s go!
基礎知識
莊子曰:“水之積也不厚,則其負大舟也無力。”。要完全掌握一個知識點,先將這個知識點需要的基礎知識補齊,這樣學的才能牢固。
下面就是我認為學習對 JSON 操作前的知識點。如果您對這部分已經瞭然於胸,儘可以略過這部分,跳到下一節。
什麼是 JSON?
Json (Javascript Object Notation) 是一種輕量級的資料交換格式,它基於 Javascript 的物件字面量。儘管它只是 Javascript 的一個子集,但它與語言無關。以現代程式語言編寫的程式,都可以用它來彼此交換資料。它是一種文字格式,人和機器都可以閱讀它。
—— 《Javascript 語言精粹》
既然以現代程式語言都可以用它來交換資料,強大的 python 當然也不例外。要更好的使用 JSON 一定要先了解下它的語法。
JSON 的語法
JSON 的值分為 6 種型別,分別是物件,陣列,字串,數字,布林值 (true
和 false
)和null
。來看一個典型的 JSON 集合,體會下這些型別。
{
"obj": {
"name": "xxx",
"address": {
"country": "china",
"city": "TianJin"
}
},
"arr_simple": [1, 2, 3, 5],
"arr_complex": [
1,
"a",
{
"b": "yyy"
},
true,
null
],
"str": "I am a string",
"num": 888,
"booValue": false,
"nullValue": null
}
看上面程式碼, JSON 語法有什麼特點呢?
- JSON 字串必須使用 雙引號包圍
- 可以在任何值前後插入空白(包括空格,製表符,回車,換行),當然這些空白符也可以去除。
像字串,數字,布林值,null 都比較簡單,無需細數,接下來我們重點來看下物件和陣列。
JSON 物件有哪些特點?
JSON 物件的結構是什麼樣子呢?
上面程式碼中的 obj
就是一個 JSON 物件,我們來觀察下它。
- 它是用
{}
括起來的一個集合,每一項都包含名稱 和值 ,如name
就是名稱,而值就是xxx
。 - 名稱可以是任意字串,但必須是字串才可以哦。
- 值只要是上面 6 種型別之一就可以
- 名稱/值 對沒有固定的順序,可以是任意順序
- 可以支援無限層的巢狀,如
obj
物件中巢狀了一個address
物件,但是為了保證處理的高效性,請儘量保持結構的扁平性,也就是不要巢狀太多層哦)
為了能夠處理 JSON 資料,許多語言都有對應的資料型別可以對映為 JSON 物件,那麼 python 中是什麼資料型別呢?
是 dict
,如果有您對 dict
有些遺忘了,就請到這裡複習下吧。
JSON 陣列有哪些特點?
上面程式碼中的 arr_simple
和 arr_complex
都表示陣列,它們有哪些特點呢?
- 是一個 有序序列
- 只有 值 組成
- 值可以是任意型別的 JSON 值,如
arr_complex
陣列。
python 也有能夠對映為 JSON 物件的資料型別,是 list
和 tuple
, 如果您對 list
和 tuple
的特性有些生疏了,也可以在這裡回顧下。
什麼是編碼和解碼?
說到 JSON 和 python 之間的轉換,就會涉及到兩個名詞:編碼 和 解碼。
那麼到底什麼是編碼和解碼呢?
編碼是資訊從一種形式或格式轉換為另一種形式的過程。解碼,是編碼的逆過程,亦即把編碼過的資訊恢復成原來樣式。
——維基百科
編碼的作用則是為了利於傳輸和儲存,JSON 當然是非常適合的。因此,
- 把 python 物件轉換成 JSON 的過程就稱為編碼
- 把 JSON 轉換成 python 物件的過程就稱為解碼
常用的 json 操作有哪些?
剛開始接觸 json 的操作,我主要有下面幾個疑問:
- json 操作需要什麼庫?
- 如何將 python 物件轉換成 JSON字串,更進一步,能不能直接轉換成檔案控制程式碼儲存到檔案中?(編碼)
- 如何將 json 字串轉換成 python 物件,更進一步,能不能直接將 JSON 格式的檔案轉換成 python 物件?(解碼)
下面就讓我們一一來探索這些問題。
json 操作需要什麼庫?
使用 json 函式前需要先匯入 json 庫:
import json
json
庫本身就是 python 內建的標準庫,因此你不需要做任何安裝的操作。只要宣告瞭上面語句,就可直接使用。
如何將 python 編碼成 JSON?
python 編碼為 JSON 的對照表
要完成這個功能,先要看下 python 資料結構編碼為 json 的對照表。
Python | JSON |
---|---|
dict | object |
list, tuple | array |
str | string |
int, float, int- & float-derived Enums | number |
True | true |
False | false |
None | null |
有了這張表,我們就可以清楚的知道 python 物件將編碼成的 json 格式。
json.dumps()
json.dumps() 方法的作用就是將 python 物件轉換成 JSON 字串,下面來看具體的函式宣告:
json.dumps(obj, 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)
引數看起來好多啊,不過不用擔心,這麼多引數,只有第一個引數是必填的。下面就來一一瞭解下這些引數的意義
- obj 就是要編碼的 python 物件
- skipkeys 預設值是
False
。設定為True
,假如obj
中的 dict keys 不是基本型別(str
,int
,float
,bool
,None
), 就會被忽略,而不是丟擲TypeError
錯誤 - ensure_ascii 預設是
True
, 表示預設使用ascii 編碼。如果 obj 內含有非 ASCII 字元,就會出現 “uXXXX” 格式顯式的資料, 設定成False
就會使用字元本來的編碼。- 這裡要注意,如果輸入是中文,需要指定
ensure_ascii=False
- 這裡要注意,如果輸入是中文,需要指定
- check_circular 預設值是
True
,如果設定為False
就不會檢查內部型別是否包含迴圈引用,而且迴圈引用會導致 OverflowError - allow_nan 預設值為
False
,如果碰到超過範圍的float
值(nan
,inf
,-inf
)就使用 (NaN
,Infinity
,-Infinity
) 替換- 如果為
True
碰到這些值則會導致ValueError
- 如果為
- indent 縮排設定
- 如果是非負整數或者 string, JSON Array 元素和物件元素將會按照設定的縮排格式化顯示
- 值為 0, 負值,或者
""
只會插入新的一行 - 值為
None
(也是預設值)會盡可能的壓縮
- separators 分隔符。
- 如果要設定它,引數需要是一個元組
(item_separator, key_separator)
- 預設值是
(`, `, `: `)
,表示 keys 之間用,
隔開,而 key 和 value 之間用:
隔開
- 如果要設定它,引數需要是一個元組
- sort_keys 預設值是
False
,如果設定成True
, dict 結構的輸出就會按照 key 來排序 - encoding 預設值是 UTF-8 用於設定 JSON 資料的編碼方式,在處理中文時這裡一定要注意。
來看一個例子
>>> import json
>>> json.dumps([`foo`, {`bar`: (`baz`, None, 1.0, 2)}])
`["foo", {"bar": ["baz", null, 1.0, 2]}]`
>>> print(json.dumps(""fooar"))
""fooar"
>>> print(json.dumps(`u1234`))
"u1234"
>>> print(json.dumps(`\`))
"\"
>>> print(json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True))
{"a": 0, "b": 0, "c": 0}
json.dump()
json.dump()
函式的作用就是將 python 物件轉換成 JSON 字串,並將其通過 fp 檔案流寫入到檔案中。來看下具體的函式宣告:
json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw)
和前面的 dumps 函式進行比較,會發現兩個函式的引數是非常相似的,而且它們的意義也都相同。來看下面的例子
>>> import json
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump([`streaming API`], io)
>>> io.getvalue()
`["streaming API"]`
如何將 JSON 解碼成 python 物件?
JSON 解碼為 python 的對照表
要完成這個功能,也先要看下 json 解碼為 python 物件的對照表
JSON | Python |
---|---|
object | dict |
array | list |
string | str |
number (int) | int |
number (real) | float |
true | True |
false | False |
null | None |
編碼對照表和解碼對照表並不是一一對應的,因此如果一個 python物件 先編碼成 JSON,再轉碼回來後得到的物件可就不一定完全相等了。
json.loads()
這個方法的作用就是將引數 s
按照上面的對照表反序列化為一個 python 物件。引數 s
可以是 str
,byte
或者byteArray
格式, 但必須要包含 JSON 文字才可以)。具體函式宣告如下:
json.loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
下面就來一一瞭解下一些常用引數的意義
- s 就是要解碼的 python 字串
- encoding 指定編碼格式
- parse_float ,預設情況下相當於 float(num_str)。如果設定為其他值,將會把一個 JSON 字串按照 float 解碼呼叫,
- parse_int ,預設情況下相當於 int(num_str),如果指定,將把每個 JSON 字串按照 int 解碼呼叫
來看下面的例子,其中最後一行就指定了 parse_float
。
>>> import json
>>> json.loads(`["foo", {"bar":["baz", null, 1.0, 2]}]`)
[`foo`, {`bar`: [`baz`, None, 1.0, 2]}]
>>> json.loads(`"\"foo\bar"`)
`"foox08ar`
>>> import decimal
>>> json.loads(`1.1`, parse_float=decimal.Decimal)
Decimal(`1.1`)
json.load()
先來看函式宣告
json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
作用是將 fp
檔案流反序列化為 python 物件,其中的引數意義和 loads
方法相同。來看一個例子。
>>> import json
>>> from io import StringIO
>>> io = StringIO(`["streaming API"]`)
>>> json.load(io)
[`streaming API`]
結語
本文主要介紹了 JSON 的定義,語法以及 JSON 的常用操作。但是並沒有涉及 JSON 處理自定義資料型別的高階內容(JSONEncoder
和JSONDecoder
),這部分內容會再後面的篇章中專門介紹。
下篇會介紹 python 的模組,敬請期待。