在當今以數據為核心的開發環境中,無論是串接網頁應用程式介面(REST API)、管理設定檔,還是進行程式間的資料交換,JSON (JavaScript Object Notation) 都扮演著舉足輕重的角色。它是一種輕量級、易於人類閱讀和編寫,同時也易於機器解析和生成的資料交換格式。其純文字的本質和結構化的特性,使其成為跨語言、跨平臺資料溝通的首選標準。
Python 憑藉其強大的生態系和簡潔的語法,內建了功能完整的 json 套件,讓我們可以輕鬆地對 JSON 資料進行序列化(Serialization)和反序列化(Deserialization)操作。本篇文章將從基礎概念出發,深入探討 Python json 套件的核心功能與進階技巧,幫助您完全掌握處理 JSON 資料的能力。
JSON 基礎與 Python 資料型態的對應關係
在操作之前,我們必須先理解 JSON 的基本結構以及它如何對應到 Python 的資料類型。JSON 資料由兩種基本結構組成:
- 物件 (Object):由大括號 {} 包圍的鍵值對 (key-value pair) 集合。鍵必須是字串,並使用雙引號 “” 包覆;值可以是string、數字、布林值、array或另一個物件。這在 Python 中直接對應到 字典 (dict)。
- 陣列 (Array):由中括號 [] 包圍的值的有序列表。值可以是任何合法的 JSON 資料類型。這在 Python 中直接對應到 串列 (list)。
特別注意:標準的 JSON 格式嚴格要求鍵(key)和字串值(string value)都必須使用 雙引號 (“),若使用單引號 (‘) 會在解析時導致錯誤。
當我們使用 Python 的 json 套件時,資料類型會依循下表進行轉換:
Python 型態 | JSON 型態 | 說明 |
---|---|---|
dict | object | 字典轉換為 JSON 物件 |
list, tuple | array | 串列或tuple轉換為 JSON array |
str | string | str字串轉換為 JSON string (在舊版Python中也包含unicode) |
int, float | number | int或浮點數轉換為 JSON 數字 |
True | true | 布林值 True 轉換為 JSON 的 true |
False | false | 布林值 False 轉換為 JSON 的 false |
None | null | Python 的 None 轉換為 JSON 的 null |
JSON 型態 | Python 型態 | 說明 |
---|---|---|
object | dict | JSON 物件轉換為 Python 字典 |
array | list | JSON array轉換為 Python 串列 |
string | str | JSON string轉換為 Python str |
number (整數) | int | JSON 整數轉換為 Python int |
number (浮點數) | float | JSON 浮點數轉換為 Python 浮點數 |
true | True | JSON 的 true 轉換為 Python 的 True |
false | False | JSON 的 false 轉換為 Python 的 False |
null | None | JSON 的 null 轉換為 Python 的 None |
核心函式:序列化與反序列化
json 套件提供了四個主要的核心函式,它們是所有操作的基礎。可以依據操作對象是「json文件」還是「json字符串」來區分。
1. 反序列化 (Deserialization):將 JSON 轉換為 Python 物件
反序列化是將 JSON 格式的資料解析成 Python 可以直接操作的資料物件(如 dict 或 list)的過程。
json.load(fp):從檔案讀取並解析 JSON
此函式用於讀取一個支援 .read() 方法的檔案類物件 (file-like object),並將其內容解析為 Python 資料物件。其完整的使用方法也接受 cls=None、object_hook=None、parse_float=None、parse_int=None、object_pairs_hook=None 等參數以進行更進階的解碼控制。
範例: 假設我們有一個 user.json json文件:
{
"id": "A001",
"name": "陳大文",
"age": 30,
"is_active": true,
"courses": ["Math", "Science"]
}
我們可以使用以下 Python 程式碼讀取它:
import json
# 使用 with open() 確保檔案會被正確關閉
with open('user.json', 'r', encoding='utf-8') as f:
# 使用 json.load() 讀取檔案物件 f
data = json.load(f)
print(type(data)) # <class 'dict'>
print(data)
print(f"使用者姓名:{data['name']}")
json.loads(s):從字串讀取並解析 JSON
loads 的 s 代表 string (字符串)。此函式用於解析一個包含 JSON 格式的字符串,也就是json字符串。
範例: 當我們從 API 接收到一個 json字符串時:
import json
# 假設這是一個從網路 API 取得的 JSON 格式字符串
json_string = '''
{
"product_id": "P001",
"price": 1200.50,
"in_stock": false,
"variants": null
}
'''
# 使用 json.loads() 解析字符串
product_data = json.loads(json_string)
print(type(product_data)) # <class 'dict'>
print(product_data)
print(f"商品價格:{product_data['price']}")
2. 序列化 (Serialization):將 Python 物件轉換成 JSON
序列化則是將 Python 資料物件(如 dict 或 list)轉換為 JSON 格式的字符串或寫入json文件中,以便儲存或傳輸。
json.dump(obj, fp, …):將 Python 物件寫入 JSON 檔案
此函式將一個 Python 資料物件 (obj) 序列化成json格式,並寫入一個支援 .write() 方法的檔案類物件 (file-like object)。
範例:
import json
python_dict = {
'name': 'oxxo',
'age': 18,
'eat': ['apple', 'orange']
}
with open('output.json', 'w', encoding='utf-8') as f:
# 將 python_dict 物件寫入檔案 f
json.dump(python_dict, f)
寫入後的 output.json 內容會是緊湊的一行:
{“name”: “oxxo”, “age”: 18, “eat”: [“apple”, “orange”]}
json.dumps(obj, …):將 Python 物件轉換為 JSON 格式字串
jsondumps 同樣是針對 string 操作。它會將 Python 資料物件序列化,並回傳一個 JSON 格式的字符串。
範例:
import json
python_dict = {
'name': 'oxxo',
'age': 18,
'eat': ['apple', 'orange']
}
# 將 python_dict 轉換為 JSON 格式字符串
json_string = json.dumps(python_dict)
print(type(json_string)) # <class 'str'>
print(json_string) # {"name": "oxxo", "age": 18, "eat": ["apple", "orange"]}
# 你可以後續將這個字符串寫入檔案
with open('output_from_dumps.json', 'w', encoding='utf-8') as f:
f.write(json_string)
美化與客製化 JSON 輸出
預設情況下,dump 和 jsondumps 產生的 json字符串是沒有換行和縮排的,以節省空間。但在開發和除錯時,格式化的 JSON 更易於閱讀。我們可以透過幾個重要的參數來客製化輸出。
indent:縮排美化
這個參數可以是一個非負整數或字串。若為正整數,代表每層縮排的空格數。當 indent=None (預設值)時,則會產生最緊湊的表示形式。
data = {'name': '測試員', 'skills': ['Python', 'SQL', 'Docker']}
# 使用 indent=4 進行縮排
print(json.dumps(data, indent=4))
# 輸出:
# {
# "name": "\u6e2c\u8a66\u54e1",
# "skills": [
# "Python",
# "SQL",
# "Docker"
# ]
# }
ensure_ascii=False:正確顯示非 ASCII 字元 (如中文字)
從上面的例子可以看到,中文字預設被轉換成了 \uXXXX 的 ASCII 編碼,這是因為 ensure_ascii=True 是預設行為。為了讓中日韓等文字能直接顯示,必須設定 ensure_ascii=False。
data = {'name': '測試員', 'skills': ['Python', 'SQL', 'Docker']}
# 同時使用 indent 和 ensure_ascii
print(json.dumps(data, indent=4, ensure_ascii=False))
# 輸出:
# {
# "name": "測試員",
# "skills": [
# "Python",
# "SQL",
# "Docker"
# ]
# }
注意:當使用 ensure_ascii=False 時,強烈建議在 open() 函式中明確指定 encoding=’utf-8’,以避免亂碼問題。
sort_keys=True:按鍵排序
設定此參數為 True 會讓字典在輸出時,根據鍵(key)的字母順序進行排序,而預設值為 sort_keys=False。這對於比較不同時間生成的 json文件或確保輸出的一致性非常有用。
data = {'c': 3, 'a': 1, 'b': 2}
print(json.dumps(data, sort_keys=True, indent=4))
# 輸出:
# {
# "a": 1,
# "b": 2,
# "c": 3
# }
separators:自訂分隔符
此參數用來控制輸出的分隔符,它必須是一個 (item_separator, key_separator) 的元組。預設情況下,當 indent 為 None 時,其值為 (‘, ‘, ‘: ‘)。若要得到最緊湊的輸出,可以設為 (‘,’, ‘:’) 來去除多餘的空格。預設參數值為 separators=None。
data = {'id': 1, 'name': 'compact', 'active': True}
# 使用最緊湊的分隔符
print(json.dumps(data, separators=(',', ':')))
# 輸出: {"id":1,"name":"compact","active":true}
常見問題 (FAQ)
Q1: json.load() 和 json.loads() 到底有什麼不同?
A1: 最主要的區別在於它們的輸入來源。json.load() (沒有 ‘s’) 讀取的是一個檔案物件 (file object),通常來自 open()。而 json.loads() (結尾有 ‘s’,代表 string) 則是解析一個字符串 (json字符串)。您可以簡單記憶為 “load from string”。
Q2: 如何讓輸出的 JSON 檔案格式化,使其更容易被人類閱讀?
A2: 在使用 json.dump() (寫入檔案) 或 jsondumps() (轉換為字串) 時,加入 indent 參數。例如 json.dump(data, file, indent=4) 會使用 4 個空格進行縮排,讓 JSON 結構一目瞭然。
Q3: 為什麼我寫入的中文在 JSON 檔案裡都變成了 \uXXXX 這樣的格式?
A3: 這不是亂碼,而是 JSON 對非 ASCII 字元的預設轉義 (escape) 行為。要解決這個問題,請在 dump() 或 dumps() 函式中加入 ensureascii=False 參數。例如:json.dump(data, file, ensureascii=False, indent=4)。同時,請務必在 open() 時指定 encoding=’utf-8’。
Q4: 如果我的 Python 物件包含 JSON 原生不支援的型態 (例如 datetime 或自訂類別物件) 時該怎麼辦?
A4: json 套件在序列化時,遇到不認識的型別會拋出 TypeError。您可以透過 default 參數提供一個自訂的轉換函式,其預設值為 default=None。這個函式會在遇到不支援的型態時被呼叫,您需要在函式內將其轉換為 JSON 可支援的型態(如字串)。更進階的使用方法是繼承 json.JSONEncoder 並覆寫其 default 方法。
import json
import datetime
def serialize_datetime(obj):
if isinstance(obj, datetime.datetime):
return obj.isoformat() # 將 datetime 物件轉為 ISO 8601 格式的字串
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
data = {'event': 'Meeting', 'time': datetime.datetime.now()}
jsonstring = json.dumps(data, default=serializedatetime, indent=4)
print(json_string)
總結
Python 的 json 套件是處理當今主流資料格式的標準工具。掌握其核心功能是每位 Python 開發者的必備技能。
- 反序列化 (JSON -> Python):使用 json.load() 處理檔案,json.loads() 處理字符串。
- 序列化 (Python -> JSON):使用 json.dump() 寫入檔案,jsondumps() 轉換為字符串。
- 客製化輸出:利用 indent 參數進行美化排版,ensure_ascii=False 確保中文等非 ASCII 字元能正常顯示,sort_keys=True 則能保證輸出的鍵順序一致。
透過靈活運用這些函式與參數,您可以自信且高效地在 Python 程式中讀取、寫入及操作任何 JSON 資料物件。