json --- JSON 編碼器和解碼器

原始碼: Lib/json/__init__.py


JSON (JavaScript Object Notation),由 RFC 7159(廢棄了 RFC 4627)和 ECMA-404 標準指定,是一種輕量級的資料交換格式。它的靈感來自於 JavaScript 物件字面量語法(儘管它不是 JavaScript 的嚴格子集 [1])。

備註

在 Python 中處理 JSON 的上下文中,“物件” (object) 一詞可能存在歧義。在 Python 中,所有值都是物件。而在 JSON 中,物件是指任何用花括號包裹的資料,類似於 Python 的字典。

警告

解析來自不可信來源的 JSON 資料時要小心。惡意的 JSON 字串可能導致解碼器消耗大量 CPU 和記憶體資源。建議限制待解析資料的大小。

此模組提供的 API 對於標準庫 marshalpickle 模組的使用者來說應該很熟悉。

編碼基本的 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({'6': 7, '4': 5}, sort_keys=True, indent=4))
{
    "4": 5,
    "6": 7
}

自定義 JSON 物件編碼

>>> import json
>>> def custom_json(obj):
...     if isinstance(obj, complex):
...         return {'__complex__': True, 'real': obj.real, 'imag': obj.imag}
...     raise TypeError(f'Cannot serialize object of {type(obj)}')
...
>>> json.dumps(1 + 2j, default=custom_json)
'{"__complex__": true, "real": 1.0, "imag": 2.0}'

解碼 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', ']']

在 shell 中使用 json 進行驗證和格式化列印

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

請參閱 命令列介面 獲取詳細文件。

備註

JSON 是 YAML 1.2 的一個子集。該模組預設設定(特別是預設的 separators 值)生成的 JSON 也是 YAML 1.0 和 1.1 的子集。因此,該模組也可以用作 YAML 序列化器。

備註

預設情況下,該模組的編碼器和解碼器會保留輸入和輸出的順序。只有當底層的容器是無序的時,順序才會丟失。

基本用法

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)

使用 Python 到 JSON 的轉換表obj 序列化為 JSON 格式的流,並寫入 fp(一個支援 .write()類檔案物件)。

備註

picklemarshal 不同,JSON 不是一個有幀協議,因此嘗試使用同一個 fp 多次呼叫 dump() 來序列化多個物件,將會導致一個無效的 JSON 檔案。

引數:
  • obj (object) – 要被序列化的 Python 物件。

  • fp (類檔案物件) – obj 將被序列化到這個類檔案物件中。 json 模組總是生成 str 物件,而不是 bytes 物件,因此 fp.write() 必須支援 str 輸入。

  • skipkeys (bool) – 如果為 True,那麼不是基本型別(str, int, float, bool, None)的鍵將被跳過,而不是引發 TypeError。預設為 False

  • ensure_ascii (bool) – 如果為 True(預設值),輸出保證將所有輸入的非 ASCII 字元轉義。如果為 False,這些字元將按原樣輸出。

  • check_circular (bool) – 如果為 False,則會跳過對容器型別的迴圈引用檢查,迴圈引用將導致 RecursionError(或更糟的情況)。預設為 True

  • allow_nan (bool) – 如果為 False,在嚴格遵守 JSON 規範的情況下,序列化超出範圍的 float 值(nan, inf, -inf)將導致 ValueError。如果為 True(預設值),則會使用它們的 JavaScript 等效項(NaN, Infinity, -Infinity)。

  • cls (JSONEncoder 的子類) – 如果設定,將使用一個自定義的 JSON 編碼器,其 default() 方法被重寫,用於序列化為自定義資料型別。如果為 None(預設值),則使用 JSONEncoder

  • indent (int | str | None) – 如果是正整數或字串,JSON 陣列元素和物件成員將以該縮排級別進行格式化列印。正整數表示每級縮排的空格數;字串(例如 "\t")用於縮排每一級。如果為零、負數或 ""(空字串),則只插入換行符。如果為 None(預設值),則使用最緊湊的表示形式。

  • separators (tuple | None) – 一個二元組:(item_separator, key_separator)。如果為 None(預設值),當 indentNone 時,separators 預設為 (', ', ': '),否則預設為 (',', ': ')。要獲得最緊湊的 JSON 表示,應指定 (',', ':') 以消除空格。

  • default (可呼叫物件 | None) – 一個函式,當遇到無法被序列化的物件時會被呼叫。它應該返回該物件的一個可被 JSON 編碼的版本,或者引發 TypeError。如果為 None(預設值),則會引發 TypeError

  • sort_keys (bool) – 如果為 True,字典將按鍵排序後輸出。預設為 False

在 3.2 版更改: 除了整數,indent 也允許使用字串。

在 3.4 版更改: 如果 indent 不為 None,則預設使用 (',', ': ')

在 3.6 版更改: 所有可選引數現在都是僅限關鍵字引數。

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)

使用 JSON 到 Python 轉換表fp 反序列化為 Python 物件。

引數:
  • fp (類檔案物件) – 一個支援 .read()文字檔案二進位制檔案,包含要反序列化的 JSON 文件。

  • cls (JSONDecoder 的子類) – 如果設定,則為自定義的 JSON 解碼器。傳遞給 load() 的額外關鍵字引數將傳遞給 cls 的建構函式。如果為 None(預設值),則使用 JSONDecoder

  • object_hook (可呼叫物件 | None) – 如果設定,此函式將被呼叫,其引數為任何已解碼的 JSON 物件字面量(一個 dict)的結果。該函式的返回值將代替 dict。此功能可用於實現自定義解碼器,例如 JSON-RPC 的類提示。預設為 None

  • object_pairs_hook (可呼叫物件 | None) – 如果設定,此函式將被呼叫,其引數為任何已解碼的 JSON 物件字面量的結果,該結果是一個有序的鍵值對列表。此函式的返回值將代替 dict。此功能可用於實現自定義解碼器。如果 object_hook 也被設定,object_pairs_hook 的優先順序更高。預設為 None

  • parse_float (可呼叫物件 | None) – 如果設定,此函式將被呼叫,其引數為每個待解碼的 JSON 浮點數的字串。如果為 None(預設值),則等效於 float(num_str)。這可用於將 JSON 浮點數解析為自定義資料型別,例如 decimal.Decimal

  • parse_int (可呼叫物件 | None) – 如果設定,此函式將被呼叫,其引數為每個待解碼的 JSON 整數的字串。如果為 None(預設值),則等效於 int(num_str)。這可用於將 JSON 整數解析為自定義資料型別,例如 float

  • parse_constant (可呼叫物件 | None) – 如果設定,此函式將被呼叫,其引數為以下字串之一:'-Infinity', 'Infinity', 或 'NaN'。當遇到無效的 JSON 數字時,這可用於引發異常。預設為 None

引發:
  • JSONDecodeError – 當被反序列化的資料不是一個有效的 JSON 文件時。

  • UnicodeDecodeError – 當被反序列化的資料不包含 UTF-8、UTF-16 或 UTF-32 編碼的資料時。

在 3.1 版更改

  • 添加了可選的 object_pairs_hook 引數。

  • parse_constant 不再對 'null'、'true'、'false' 呼叫。

在 3.6 版更改

在 3.11 版更改: int() 的預設 parse_int 現在透過直譯器的整數到字串轉換長度限制來限制整數字符串的最大長度,以幫助避免拒絕服務攻擊。

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

load() 相同,但不是從一個類檔案物件中反序列化,而是從 s(一個包含 JSON 文件的 strbytesbytearray 例項)反序列化為 Python 物件,使用此轉換表

在 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

物件

dict

array

list

string

str

number (int)

int

number (real)

浮點數

true

True

false

False

None

它還能將 NaNInfinity-Infinity 理解為它們對應的 float 值,這超出了 JSON 規範的範圍。

object_hook 是一個可選函式,它將被呼叫,其引數為每個已解碼的 JSON 物件的結果,並且它的返回值將代替給定的 dict。這可用於提供自定義的反序列化(例如,支援 JSON-RPC 類提示)。

object_pairs_hook 是一個可選函式,它將被呼叫,其引數為每個已解碼的 JSON 物件的結果,該結果是一個有序的鍵值對列表。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)。

parse_constant 是一個可選函式,它將被呼叫,其引數為以下字串之一:'-Infinity''Infinity''NaN'。當遇到無效的 JSON 數字時,這可用於引發異常。

如果 strict 為 false(預設為 True),則允許在字串中出現控制字元。在此上下文中,控制字元是指字元程式碼在 0-31 範圍內的字元,包括 '\t'(製表符)、'\n''\r''\0'

如果正在反序列化的資料不是一個有效的 JSON 文件,將引發 JSONDecodeError

在 3.6 版更改: 所有引數現在都是僅限關鍵字引數。

decode(s)

返回 s(一個包含 JSON 文件的 str 例項)的 Python 表示形式。

如果給定的 JSON 文件無效,將引發 JSONDecodeError

raw_decode(s)

s(一個以 JSON 文件開頭的 str)中解碼一個 JSON 文件,並返回一個包含 Python 表示形式和文件在 s 中結束位置索引的二元組。

這可用於從一個末尾可能帶有額外資料的字串中解碼 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

物件

list, tuple

array

str

string

int, float, int- & float-derived Enums

True

true

False

false

None

在 3.4 版更改: 增加了對 int 和 float 派生的 Enum 類的支援。

要擴充套件此類以識別其他物件,請繼承並實現 default() 方法,該方法如果可能,應為 o 返回一個可序列化的物件,否則應呼叫超類的實現(以引發 TypeError)。

如果 skipkeys 為 false(預設值),當嘗試編碼非 strintfloatboolNone 的鍵時,將引發 TypeError。如果 skipkeys 為 true,這類項將被簡單地跳過。

如果 ensure_ascii 為 true(預設值),輸出保證將所有輸入的非 ASCII 字元轉義。如果 ensure_ascii 為 false,這些字元將按原樣輸出。

如果 check_circular 為 true(預設值),那麼在編碼過程中會對列表、字典和自定義編碼物件進行迴圈引用檢查,以防止無限遞迴(這會導致 RecursionError)。否則,不會進行此類檢查。

如果 allow_nan 為 true(預設值),那麼 NaNInfinity-Infinity 將被編碼。這種行為不符合 JSON 規範,但與大多數基於 JavaScript 的編碼器和解碼器一致。否則,編碼此類浮點數將引發 ValueError

如果 sort_keys 為 true(預設:False),那麼字典的輸出將按鍵排序;這對於迴歸測試很有用,可以確保 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)。

例如,要支援任意迭代器,可以像這樣實現 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,並逐個產生可用的字串表示。例如

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

異常

exception json.JSONDecodeError(msg, doc, pos)

ValueError 的子類,具有以下附加屬性

msg

未格式化的錯誤訊息。

doc

正在解析的 JSON 文件。

pos

doc 中解析失敗的起始索引。

lineno

對應於 pos 的行號。

colno

對應於 pos 的列號。

在 3.5 版本加入。

標準合規性與互操作性

JSON 格式由 RFC 7159ECMA-404 規定。本節詳細說明了此模組與 RFC 的合規程度。為簡單起見,不考慮 JSONEncoderJSONDecoder 的子類,以及未明確提及的引數。

此模組並未嚴格遵守 RFC,實現了一些在 JavaScript 中有效但在 JSON 中無效的擴充套件。特別是:

  • 接受並輸出無窮大和 NaN 數值;

  • 接受物件內的重複名稱,並且只使用最後一個鍵值對的值。

由於 RFC 允許符合 RFC 的解析器接受不符合 RFC 的輸入文字,因此該模組的反序列化器在預設設定下技術上是符合 RFC 的。

字元編碼

RFC 要求 JSON 必須使用 UTF-8、UTF-16 或 UTF-32 表示,其中 UTF-8 是為實現最大互操作性而推薦的預設編碼。

RFC 允許但不要求,該模組的序列化器預設設定 ensure_ascii=True,從而對輸出進行轉義,使生成的字串只包含 ASCII 字元。

除了 ensure_ascii 引數外,此模組嚴格定義了 Python 物件和 Unicode 字串 之間的轉換,因此沒有直接處理字元編碼問題。

RFC 禁止在 JSON 文字的開頭新增位元組順序標記(BOM),此模組的序列化器不會在其輸出中新增 BOM。RFC 允許但不要求 JSON 反序列化器忽略其輸入中的初始 BOM。此模組的反序列化器在存在初始 BOM 時會引發 ValueError

RFC 並未明確禁止包含無效 Unicode 字元(例如,未配對的 UTF-16 代理對)的位元組序列的 JSON 字串,但它確實指出這可能會導致互操作性問題。預設情況下,此模組接受並輸出(當存在於原始 str 中時)此類序列的程式碼點。

無窮大和 NaN 數值

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 物件內的名稱應是唯一的,但未強制規定如何處理 JSON 物件中的重複名稱。預設情況下,此模組不會引發異常;相反,它會忽略給定名稱的所有鍵值對,只保留最後一個。

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

可以使用 object_pairs_hook 引數來改變此行為。

頂層的非物件、非陣列值

由已廢棄的 RFC 4627 規定的舊版 JSON 要求 JSON 文字的頂層值必須是 JSON 物件或陣列(Python 的 dictlist),而不能是 JSON 的 null、布林值、數字或字串值。RFC 7159 取消了該限制,此模組的序列化器和反序列化器從未實現過該限制。

儘管如此,為了獲得最大的互操作性,你可能希望自己遵守該限制。

實現限制

一些 JSON 反序列化器實現可能會對以下內容設定限制:

  • 可接受的 JSON 文字的大小

  • JSON 物件和陣列的最大巢狀層級

  • JSON 數字的範圍和精度

  • JSON 字串的內容和最大長度

此模組除了相關 Python 資料型別本身或 Python 直譯器本身的限制外,不施加任何此類限制。

在序列化為 JSON 時,請注意可能消費你的 JSON 的應用程式中的任何此類限制。特別是,JSON 數字通常被反序列化為 IEEE 754 雙精度浮點數,因此會受到該表示的範圍和精度限制。這在序列化極大數量級的 Python int 值,或序列化“特殊”數值型別(如 decimal.Decimal)的例項時尤其重要。

命令列介面

原始碼: Lib/json/tool.py


json 模組可以透過 python -m json 作為指令碼呼叫,以驗證和格式化列印 JSON 物件。json.tool 子模組實現了此介面。

如果未指定可選的 infileoutfile 引數,將分別使用 sys.stdinsys.stdout

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

在 3.5 版更改: 輸出現在與輸入順序相同。使用 --sort-keys 選項按鍵的字母順序對字典輸出進行排序。

在 3.14 版更改: json 模組現在可以直接執行為 python -m json。為了向後相容,仍然支援將 CLI 呼叫為 python -m json.tool

命令列選項

infile

要驗證或格式化列印的 JSON 檔案

$ python -m json 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.html#sys.stdout" title="sys.stdout">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

顯示幫助資訊。

腳註