06.26 Python 模塊 JSON

json 模塊可以把一個 Python 對象編碼為一個 JSON 字符串,還可以把 JSON 字符串解析為一個 Python 對象。

json 模塊提供的功能和 pickle 模塊類似,接口 API 也類似。pickle 模塊是把對象序列化為一串字節流,可以保存或者傳入另一個進程。json 模塊是把對象編碼為大家所熟知的 JavaScript 對象表達式 JSON(JavaScript Object Notation),在其他語言中很多都實現了 JSON 格式。它被大量應用於服務端、客戶端通訊的接口,如:REST API,或者跨進程通訊的應用。

編碼和解碼數據 Encoding and Decoding

dumps() 方法把一個 Python 對象 轉化為 JSON。

執行:

loads() 方法把一個 JSON 字符串轉換為 Python 對象。

執行:

注意,解析 JSON 字符串生成的對象,鍵 d 是個列表類型,之前的對象是一個元組。

美化、壓縮輸出

JSON 字符串的可讀性要比 pickle 生成的二進制要好。dumps() 方法可接收多個參數,使輸出更方便閱讀,例如 sort_keys 標誌告訴編碼器按照字典排序。

執行:

查看輸出,傳入 sort_keys=True 後, 鍵 a, c, d 按照字典順序排序了。

如果是嵌套比較多的數據結構,可以使用 indent 參數指定一個整數值,代表縮進數量。

執行:

上面的例子,使用了 4 個單位長的縮進量。

separators 參數指定 JSON 字符串的分隔符,默認的分隔符是:(', ', ': '),逗號和冒號後面都有空格,請看下面的例子:

執行:

可以看到,指定了分隔符的輸出字符數量減少了,這樣可以用在生產環境中,減少傳輸的字節數量。

編碼類型錯誤 TypeError

如果編碼一個字典,字典類型的鍵 key 如果不是一個字符串,而是一個元組,會觸發類型錯誤 TypeError 的異常。

元組可以作為字典的鍵,因為元組是不可變的。而列表是可變的,則不可以作為字典的鍵。

執行:

本例中,列表中的字典有一個鍵是元組,默認調用 dumps() 方法,觸發了 TypeError 異常,提示鍵必須是字符串。後面使用 skipkeys 參數避免了觸發異常,元組的鍵值並沒有輸出。

編碼自定義類型

上面的例子都是 Python 的內置(built-in)類型,還有可能要編碼自定義的類型。例如給出下面自定義的一個類型:

上面定義了模塊 user,其中包含要編碼的自定義類型: User。

通過下面的代碼,我們就可以編碼自定義的類型了,請看:

執行:

查看輸出,第一次觸發異常 TypeError,提示 <user> 不能 JSON。第二次傳入了 default 參數,是一個 convert 函數,這個函數告訴編碼器怎麼編碼自定義類型 User。最後返回的 JSON 就是 convert 返回的字典信息。/<user>

下面看一下,怎麼使用 object_hook 參數把一個 JSON 字符串轉換為自定義類型對象,object_hook 在解析的時候,把一個字典對象轉換為自定義對象,所以最後他返回的就是自定義對象而不是字典。

執行:

查看輸出,使用 object_hook 正確返回了自定義對象 User,注意 to_user() 函數首先使用了字典的 __class__ 和 __module__ 信息導入了 user 模塊,然後使用了剩餘的字典鍵值對,使用 **kwargs 語法實例化了自定義類型。

使用流和文件 Streams and Files

上面的例子,編碼的數據結構都是存在內存中。json 還提供了便利的方法 load() 和 dump() 接收文件類型的對象(file-like object)。

執行:

上例中,使用 io 模塊構造了一個文件對象,dump() 方法的第二個參數是文件對象,會把數據寫入到文件對象,最後調用 getvalue() 方法打印了 JSON 字符串。

下面的例子,使用 load() 函數從文件讀取 JSON 串轉換為對象。

執行: