json --- JSON 編碼器與解碼器

原始碼:Lib/json/__init__.py


JSON (JavaScript Object Notation) 是一個輕量化的資料交換格式,在 RFC 7159(其廢棄了 RFC 4627)及 ECMA-404 裡面有詳細說明,它啟發自 JavaScript 的物件字面語法 (object literal syntax)(雖然它並不是 JavaScript 的嚴格子集 [1])。

警告

當剖析無法信任來源的 JSON 資料時要小心。一段惡意的 JSON 字串可能會導致解碼器耗費大量 CPU 與記憶體資源。建議限制剖析資料的大小。

json 為習慣標準函式庫 marshalpickle 模組的使用者提供熟悉的 API。

對基本 Python 物件階層進行編碼:

>>> import json
>>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
'["foo", {"bar": ["baz", null, 1.0, 2]}]'
>>> print(json.dumps("\"foo\bar"))
"\"foo\bar"
>>> 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}
>>> from io import StringIO
>>> io = StringIO()
>>> json.dump(['streaming API'], io)
>>> io.getvalue()
'["streaming API"]'

改用緊湊型編碼方式:

>>> import json
>>> json.dumps([1, 2, 3, {'4': 5, '6': 7}], separators=(',', ':'))
'[1,2,3,{"4":5,"6":7}]'

美化輸出:

>>> import json
>>> print(json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=4))
{
    "4": 5,
    "6": 7
}

JSON 解碼:

>>> import json
>>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
['foo', {'bar': ['baz', None, 1.0, 2]}]
>>> json.loads('"\\"foo\\bar"')
'"foo\x08ar'
>>> from io import StringIO
>>> io = StringIO('["streaming API"]')
>>> json.load(io)
['streaming API']

自訂特殊的 JSON 解碼方式:

>>> import json
>>> def as_complex(dct):
...     if '__complex__' in dct:
...         return complex(dct['real'], dct['imag'])
...     return dct
...
>>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
...     object_hook=as_complex)
(1+2j)
>>> import decimal
>>> json.loads('1.1', parse_float=decimal.Decimal)
Decimal('1.1')

繼承 JSONEncoder 類別並自行擴充額外的編碼方法:

>>> import json
>>> class ComplexEncoder(json.JSONEncoder):
...     def default(self, obj):
...         if isinstance(obj, complex):
...             return [obj.real, obj.imag]
...         # Let the base class default method raise the TypeError
...         return super().default(obj)
...
>>> json.dumps(2 + 1j, cls=ComplexEncoder)
'[2.0, 1.0]'
>>> ComplexEncoder().encode(2 + 1j)
'[2.0, 1.0]'
>>> list(ComplexEncoder().iterencode(2 + 1j))
['[2.0', ', 1.0', ']']

在命令列介面裡使用 json.tool 來驗證 JSON 語法和美化呈現方式:

$ echo '{"json":"obj"}' | python -m json.tool
{
    "json": "obj"
}
$ echo '{1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

更詳盡的文件請見 命令列介面

備註

JSON 語法是 YAML 1.2 語法的一種子集合。所以如果使用預設的設定的話(準確來說,使用預設的 separators 分隔符設定的話),這個模組的輸出也符合 YAML 1.0 和 1.1 的子集合規範。因此你也可以利用這個模組來當作 YAML 的序列化工具(serializer)。

備註

這個模組的編、解碼器預設會保存輸入與輸出資料的順序關係,除非一開始的輸入本身就是無序的。

基本用法

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)

參考這個轉換表obj 序列化為符合 JSON 格式的串流,並寫入到 fp (一個支援 .write() 方法的 file-like object

如果 skipkeys 被設為 true(預設值:False),那麼非基本型別(strintfloatboolNone)的 dictionary(字典)鍵值將被略過而不會引發 TypeError

json 模組總是產生 str 物件,而非 bytes 物件。因此,fp.write() 必須支援 str 輸入。

如果 ensure_ascii 被設為 true(預設值),則輸出時將確保所有輸入的非 ASCII 字元都會被轉義。若 ensure_ascii 為 false,則這些字元將照原樣輸出。

如果 check_circular 設為 false(預設是 True),則針對不同容器型別的循環參照 (circular reference) 檢查將會被跳過,若有循環參照則最後將引發 RecursionError (或其他更糟的錯誤)。

如果 allow_nan 為 false(預設值:True),則序列化不符合嚴格 JSON 規範的 float 值 (nan, inf, -inf) 會引發 ValueError。如果 allow_nan 為 true,則將使用它們的 JavaScript 等效表示 (NaN, Infinity, -Infinity)。

如果 indent 是非負整數或字串,則 JSON 陣列元素和物件成員將使用該縮排等級進行格式美化。縮排等級 0、負數或 "" 只會插入換行符號。None(預設值)等於是選擇最緊湊的表示法。使用正整數縮排可以在每層縮排數量相同的空格。如果 indent 是一個字串(例如 "\t"),則該字串用於縮排每個層級。

在 3.2 版的變更: 除了整數之外,indent 還允許使用字串作為輸入。

如果有指定本引數內容,separators 應該是一個 (item_separator, key_separator) 二元組。如果 indentNone 則預設為 (', ', ': '),否則預設為 (',', ': ')。想要獲得最緊湊的 JSON 表示形式,你可以改成指定 (',', ':') 來消除空格。

在 3.4 版的變更: 如果 indent 不是 None,則使用 (',', ': ') 作為預設值

如果有指定本參數,default 會是一個遭遇無法序列化的物件時會被呼叫的函式。它應該回傳該物件的 JSON 可編碼版本或引發 TypeError。如果未指定,則會直接引發 TypeError

如果 sort_keys 為 true(預設值:False),則字典的輸出將按鍵值排序。

若要使用繼承自 JSONEncoder 的自訂子類別(例如覆寫 default() 方法來序列化其他型別的一個子類別物件),請使用關鍵字引數 cls 指定該類別物件;否則預設使用 JSONEncoder

在 3.6 版的變更: 所有可選參數現在都是僅限關鍵字參數了。

備註

picklemarshal 不同,JSON 不具有二進位分框(binary framed)的協定,因此嘗試重複呼叫 dump() 來序列化多個物件到同一個 fp 裡將導致無效的 JSON 檔案。

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 格式 str。這個引數的作用與 dump() 中的同名引數意義相同。

備註

JSON 鍵/值對中的鍵始終為 str 型別。當字典被轉換為 JSON 時,字典的所有鍵值資料型別都會被強制轉換為字串。因此,如果將字典先轉換為 JSON 格式然後再轉換回字典,則該字典可能不等於原始字典。也就是說,如果字典 x 含有非字串鍵值,則 loads(dumps(x)) != x

json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

使用此轉換表來將 fp(一個支援 .read()、包含 JSON 文件的文字檔案二進位檔案)去序列化為 Python 物件。

object_hook 是一個可選引數,其接受一個函式作為輸入。原始的字串解碼結果(一個 dict)將被傳入這個函式、並使用 object_hook 的回傳值來取代原先的 dict 輸出。此功能可用於實作自訂解碼器(例如 JSON-RPC 類別提示)。

object_pairs_hook 是一個可選引數,其接受一個函式作為輸入。原始的有序對串列(ordered list of pairs)解碼結果將被傳入這個函式、並使用 object_pairs_hook 的回傳值來取代原先的 dict 輸出。此功能可用於實作自訂解碼器。如果也同時給定了 object_hook,則 object_pairs_hook 優先。

在 3.1 版的變更: 新增對於 object_pairs_hook 的支援。

parse_float 為可選函式,每個要被解碼的 JSON 浮點數字串都會改用這個參數給定的函式來進行解碼。預設情況這等效於 float(num_str)。這個參數可用於將 JSON 中的浮點數解碼或剖析為另一種資料型別(例如 decimal.Decimal)。

parse_int 為可選函式,當解碼 JSON 整數字串時會被呼叫。預設情況等效於 int(num_str)。這個參數可用於將 JSON 中的整數解碼或剖析為另一種資料型別(例如 float)。

在 3.11 版的變更: 預設 parse_int 使用的 int() 函式現在有限制整數字串的長度上限了,限制由直譯器的整數字串轉換長度限制機制來達成,這能防止阻斷服務攻擊 (Denial of Service attacks)。

parse_constant 為可選函式,在解碼時若遭遇字串 '-Infinity''Infinity''NaN' 其中之一則會改用這個參數給定的函式來進行解碼。這也可用於使解碼過程中遇到無效的 JSON 數字時引發一個例外。

在 3.1 版的變更: 遭遇 'null'、'true' 或 'false' 時不再以 parse_constant 給定的函式來處理了。

若想要使用自訂的 JSONDecoder 子類別物件,請以 cls 關鍵字引數指定之,否則將使用預設的 JSONDecoder。其他未使用到的關鍵字引數將繼續傳入給 JSONDecoder 的建構函式使用。

如果被去序列化(deserialized)的資料不符合 JSON 格式,將會引發 JSONDecodeError 例外。

在 3.6 版的變更: 所有可選參數現在都是僅限關鍵字參數了。

在 3.6 版的變更: 現在,fp 可以是一個二進位檔案,前提是其編碼格式為 UTF-8、UTF-16 或 UTF-32。

json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)

使用轉換表s (一個含有 JSON 文件的 strbytesbytearray 的實例(instance))去序列化(deserialize)為一個 Python 物件

其餘引數的使用方式與意義和 load() 的相同。

如果被去序列化(deserialized)的資料不符合 JSON 格式,將會引發 JSONDecodeError 例外。

在 3.6 版的變更: 現在,s 可以是一個二進位檔案如 bytesbytearray,前提是其編碼格式為 UTF-8、UTF-16 或 UTF-32。

在 3.9 版的變更: 刪除關鍵字引數 encoding

編碼器與解碼器

class json.JSONDecoder(*, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, strict=True, object_pairs_hook=None)

簡易 JSON 解碼器

預設將執行下列資料型別轉換:

JSON

Python

object

dict

array

list

string

str

number (整數)

int

number (實數)

float

true

True

false

False

null

None

雖然 NaNInfinity-Infinity 並不符合 JSON 規範,但解碼器依然能正確地將其轉換到相應的 Python float 值。

object_hook 是一個可選函式,其接受一個解碼後的 JSON 物件作為輸入,並使用其回傳值來取代原先的 dict。這個功能可用於提供自訂的去序列化(例如支援 JSON-RPC 類別提示)。

object_pairs_hook is an optional function that will be called with the result of every JSON object decoded with an ordered list of pairs. The return value of object_pairs_hook will be used instead of the dict. This feature can be used to implement custom decoders. If object_hook is also defined, the object_pairs_hook takes priority.

在 3.1 版的變更: 新增對於 object_pairs_hook 的支援。

parse_float 為可選函式,每個要被解碼的 JSON 浮點數字串都會改用這個參數給定的函式來進行解碼。預設情況這等效於 float(num_str)。這個參數可用於將 JSON 中的浮點數解碼或剖析為另一種資料型別(例如 decimal.Decimal)。

parse_int 為可選函式,當解碼 JSON 整數字串時會被呼叫。預設情況等效於 int(num_str)。這個參數可用於將 JSON 中的整數解碼或剖析為另一種資料型別(例如 float)。

parse_constant 為可選函式,在解碼時若遭遇字串 '-Infinity''Infinity''NaN' 其中之一則會改用這個參數給定的函式來進行解碼。這也可用於使解碼過程中遇到無效的 JSON 數字時引發一個例外。

如果 strict 被設為 false(預設值為 True),那麼字串中將允許控制字元。此語境中的控制字元指的是 ASCII 字元編碼在 0~31 範圍內的字元,包括 '\t'``(tab)、'n''r'`` 和 '\0'

如果被去序列化(deserialized)的資料不符合 JSON 格式,將會引發 JSONDecodeError 例外。

在 3.6 版的變更: 所有參數現在都是僅限關鍵字參數了。

decode(s)

回傳用 Python 型式表達的 s (一個含有 JSON 文件的 str 實例)。

若給定的輸入不符合 JSON 格式會引發 JSONDecodeError 例外。

raw_decode(s)

s (一個開頭部分含有合格 JSON 文件的 str) 解碼,並將 JSON 文件結束點的索引值(index)和解碼結果合併為一個二元組(2-tuple)後回傳。

這個方法可以用來解碼尾段可能帶有 JSON 以外資料的文字。

class json.JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)

可擴充的 Python 資料結構 JSON 編碼器。

預設可支援下列物件及型別:

Python

JSON

dict

object

list, tuple

array

str

string

int、float 或可作為整數或浮點數運算的衍生列舉(int- or float-derived Enums)

number

True

true

False

false

None

null

在 3.4 版的變更: 增加對整數(int)、浮點數(float)或可作為整數或浮點數運算的衍生列舉(int- or float-derived Enums)類別的支援性。

若要擴充此功能來識別其他物件,請繼承並實作一個 default() 方法。此方法應回傳一個可序列化的 o 物件,否則此方法應呼叫父類別的 JSONEncoder.default 方法(以引發 TypeError 例外)。

skipkeys 為 false(預設值),則當在編碼不是 strintfloatNone 的鍵值時,將引發 TypeError。如果 skipkeys 為 true,這些項目將直接被跳過。

如果 ensure_ascii 被設為 true(預設值),則輸出時將確保所有輸入的非 ASCII 字元都會被轉義。若 ensure_ascii 為 false,則這些字元將照原樣輸出。

如果 check_circular 為 true(預設值),則會在編碼期間檢查串列(list)、字典(dict)和自訂編碼物件的循環參照,以防止無限遞迴(一個會導致 RecursionError 例外的問題)。否則不會進行此類檢查。

如果 allow_nan 為 true(預設值),則 NaNInfinity-Infinity 將按照原樣進行編碼。請記得此行為不符合標準 JSON 規範,但的確與大多數基於 JavaScript 的編碼器和解碼器一致。否則若設為 false,嘗試對這些浮點數進行編碼將引發 ValueError 例外。

如果 sort_keys 為 true(預設值:False),則 dictionary(字典)的輸出將按鍵值排序。這項功能可確保 JSON 序列化的結果能被互相比較,能讓日常的回歸測試檢查變得方便一些。

如果 indent 是非負整數或字串,則 JSON 陣列元素和物件成員將使用該縮排等級進行格式美化。縮排等級 0、負數或 "" 只會插入換行符號。None(預設值)等於是選擇最緊湊的表示法。使用正整數縮排可以在每層縮排數量相同的空格。如果 indent 是一個字串(例如 "\t"),則該字串用於縮排每個層級。

在 3.2 版的變更: 除了整數之外,indent 還允許使用字串作為輸入。

如果有指定本引數內容,separators 應該是一個 (item_separator, key_separator) 二元組。如果 indentNone 則預設為 (', ', ': '),否則預設為 (',', ': ')。想要獲得最緊湊的 JSON 表示形式,你可以改成指定 (',', ':') 來消除空格。

在 3.4 版的變更: 如果 indent 不是 None,則使用 (',', ': ') 作為預設值

如果有指定本參數,default 會是一個遭遇無法序列化的物件時會被呼叫的函式。它應該回傳該物件的 JSON 可編碼版本或引發 TypeError。如果未指定,則會直接引發 TypeError

在 3.6 版的變更: 所有參數現在都是僅限關鍵字參數了。

default(o)

在任意一個子類別裡實作這個方法時須讓其回傳一個可序列化的物件 o ,或呼叫原始的實作以引發 TypeError 例外。

舉例來說,想要讓編碼器支援任意疊代器(iterator),你可以實作這樣子的 default()

def default(self, o):
   try:
       iterable = iter(o)
   except TypeError:
       pass
   else:
       return list(iterable)
   # Let the base class default method raise the TypeError
   return super().default(o)
encode(o)

回傳一個 Python 資料結構物件 o 的 JSON 的字串表示。例如:

>>> json.JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'
iterencode(o)

將物件 o 編碼,並將結果統整為一個能依序產生(yield)各結果字串的物件。如下例:

for chunk in json.JSONEncoder().iterencode(bigobject):
    mysocket.write(chunk)

例外

exception json.JSONDecodeError(msg, doc, pos)

ValueError 的子類別具有下列額外屬性:

msg

未受格式化的錯誤訊息。

doc

正在被剖析的 JSON 文件。

pos

doc 剖析失敗處的起始點的索引值。

lineno

pos 所在的列(line)數。

colno

pos 所在的行(column)數。

在 3.5 版被加入.

合規性與互通性(Interoperability)

JSON 格式是由 RFC 7159ECMA-404 規範的。本節詳細說明了本模組對 RFC 的遵循程度。簡單起見,JSONEncoderJSONDecoder 子類別以及未明確提及的參數將不予討論。

這個模組的部份實作並未非常嚴格地遵循 RFC 規範。準確來說,下列實際實作符合 JavaScript 語法格式,但並不符合 JSON 格式:

  • 無限(Infinite)和非數字(NaN)值會被接受。

  • 同一個物件內可以有重複的名稱,但只有最後一個同名物件是有效的。

不過 RFC 准許遵循 RFC 的剖析器接受不合規的文字輸入,所以技術上來說若以預設設定運作,本模組的去序列化器(deserializer)是符合 RFC 規範的。

字元編碼格式

RFC 要求 JSON 必須以 UTF-8、UTF-16 或 UTF-32 格式編碼。並推薦以 UTF-8 編碼以達成最佳的互通性。

RFC 准許但並不強制編碼器的 ensure_ascii=True 行為是預設值,但本模組依然實作了此一選項作為預設,因此本模組預設會轉義所有非 ASCII 字元。

除了 ensure_ascii 選項參數之外,本模組嚴格遵循 Python 物件與 Unicode strings 之間的轉換規範,因此並不另外處理字元編碼的問題。

RFC 禁止在文件的開頭加上端序記號(Byte Order Mark),因此本模組的序列化器(serializer)也不會在輸出中加入端序記號。RFC 允許但不強制 JSON 去序列化器(deserializer)忽略文件初始的端序記號,因此本模組的去序列化器將在遭遇位於文件開頭的端序記號時引發 ValueError 例外。

RFC 並未明確禁止 JSON 文件包含無法對應有效 Unicode 字元的位元組序列(例如未配對的 UTF-16 代理對(surrogate pairs)),但這個特性的確可能會引起相容性問題。預設情況下,當原始輸入的 str 中存在此類序列時,該模組將接受並輸出這些序列的編碼位置(code points)。

正負無限與非數值

RFC 不允許表現無限大或非數值(NaN)。但預設情況下,這個模組仍接受並輸出 Infinity-InfinityNaN,如同它們是有效的 JSON 數值字面值:

>>> # Neither of these calls raises an exception, but the results are not valid JSON
>>> json.dumps(float('-inf'))
'-Infinity'
>>> json.dumps(float('nan'))
'NaN'
>>> # Same when deserializing
>>> json.loads('-Infinity')
-inf
>>> json.loads('NaN')
nan

在序列化器中,allow_nan 參數可以改變這個行為。在去序列化器中,parse_constant 參數可以改變這個行為。

物件內重複的名稱

RFC 規範僅表明 JSON 物件中的名字應該是唯一的,但沒有強制要求如何處理重複的名字。預設情況下,本模組不會因此引發例外;相反的,它會忽略該名字的所有重複鍵值對,並只保留最後一個:

>>> weird_json = '{"x": 1, "x": 2, "x": 3}'
>>> json.loads(weird_json)
{'x': 3}

object_parts_hook 參數可以改變這個行為。

位於頂層的非物件及非列表值

由已廢棄的 RFC 4627 所規範的舊版 JSON 要求 JSON 文字的頂層值必須是 JSON 物件或陣列(Python dictlist),而且不能是 JSON 的 null、boolean、數字或字串值。 RFC 7159 移除了這個限制,而本模組的序列化器或去串列化器中未曾實施過該限制。

如果想要最大限度地保留互通性,你可能還是會想要自行施加這個限制。

實作限制

某些 JSON 去序列化器的實作可能會造成下列限制:

  • JSON 文件長度上限

  • JSON 物件或陣列的最大巢狀層數(level of nesting)限制

  • 數字的精準度或範圍

  • JSON 字串長度上限

本模組除了 Python 資料型態本身或 Python 直譯器本身的限制以外,不會設定任何此類限制。

將資料序列化為 JSON 時,要注意可能會使用該 JSON 輸出的應用程式中的相關限制。特別要注意的是,JSON 數字常會被去序列化為 IEEE 754 雙精度浮點數(double),並因而受到其表示範圍和精度限制的影響。這在序列化極大的 Python int 數值、或是序列化特殊數字型別的實例時(例如 decimal.Decimal)尤其重要。

命令列介面

原始碼:Lib/json/tool.py


json.tool 模組提供了一個簡易的命令列界面以供校驗與美化呈現 JSON 物件。

如果沒有指定可選引數 infileoutfile ,則 sys.stdinsys.stdout 將各自做為輸入和輸出的預設值。

$ echo '{"json": "obj"}' | python -m json.tool
{
    "json": "obj"
}
$ echo '{1.2:3.4}' | python -m json.tool
Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

在 3.5 版的變更: 現在開始輸出和輸入的資料順序會是相同的。傳入 --sort-keys 引數以按照鍵值的字母順序對輸出進行排序。

命令列選項

infile

將被用於校驗或美化呈現的 JSON 文件:

$ python -m json.tool mp_films.json
[
    {
        "title": "And Now for Something Completely Different",
        "year": 1971
    },
    {
        "title": "Monty Python and the Holy Grail",
        "year": 1975
    }
]

如果沒有指定 infile 則會從 sys.stdin 讀取輸入。

outfile

infile 的結果寫入到給定的 outfile。若未提供則寫入到 sys.stdout

--sort-keys

按照鍵值的字母順序對輸出字典進行排序。

在 3.5 版被加入.

--no-ensure-ascii

關閉非 ASCII 字元的自動轉義功能。詳情請參照 json.dumps()

在 3.9 版被加入.

--json-lines

將每一行輸入都單獨輸出為一個 JSON 物件。

在 3.8 版被加入.

--indent, --tab, --no-indent, --compact

互斥的空白字元控制選項。

在 3.9 版被加入.

-h, --help

顯示說明訊息。

註解