在現代網路應用與微服務架構中,資料交換成為各系統溝通的橋樑,而 JSON(JavaScript Object Notation)作為一種輕量且具高度可讀性的資料格式,正逐步取代 XML 成為主流。無論是在前端傳輸資料,還是後端 API 的回應,都離不開 JSON 的身影。對於 Python 開發者而言,掌握如何讀取、解析、編碼與儲存 JSON 資料,是提升專案效率與可靠性的必要技能。
本文章將從 JSON 的基本定義、結構與語法規則開始,接著詳細介紹在 Python 中 json 模組、套件,並搭配豐富範例展示如何處理 JSON 資料物件。除此之外,我們還會探討進階應用(例如自定義編碼與解碼)、命令列工具的使用,以及在實際專案中的注意事項與常見問題解析。希望通過本篇文章,你能夠深入了解 JSON 的工作原理,並靈活運用在各種實際情境中。
一、什麼是 JSON?
JSON 的定義與歷史
JSON 檔的全名為 JavaScript Object Notation,是由 Douglas Crockford 於 2001 年最先提出。儘管其語法靈感來自 JavaScript 的物件字面量,但 JSON 格式已經脫離了語言的限制,成為一種獨立的資料交換格式。它具有以下特點:
- 輕量型(Light-Weight): 所生成的文件體積小,適合網路傳輸,減少頻寬佔用。
- 基於文本(Text-Based): 以純文字呈現,無需特殊工具即可編輯和檢視字符串。
- 可讀性高(Human-Readable): 結構清晰、語法簡單,方便人員閱讀與理解的字符串。
- 穩定性(Stability): JSON 格式自誕生以來就非常穩定,即使多年後,舊有的 JSON 檔案仍能被現行系統正確解析。
正因為這些優點,JSON 成為 REST API 與許多網路應用的首選資料格式。
JSON 的基本語法與結構
JSON 主要由兩種結構組成:物件(Object) 與 陣列(Array)。
物件(Object)
物件由一對大括號 {} 包圍,其中包含以逗號分隔的多組「鍵(Key)」與「值(Value)」對。每個鍵必須是字串(必須用雙引號 ” 包圍),鍵與值之間用冒號 : 分隔。
範例:
{
"name": "John",
"age": 30,
"city": "New York"
}
陣列(Array)
陣列由一對中括號 [] 包圍,其中的每個元素可以是任何合法的 JSON 資料型態(物件、陣列、字串、數字、布林值或 null),元素間用逗號 , 分隔。
範例:
[
"apple",
"banana",
"orange"
]
綜合範例
以下是一個包含物件與陣列的綜合範例:
{
"name": "John",
"age": 30,
"city": "New York",
"children": [
{
"name": "Alice",
"age": 5
},
{
"name": "Bob",
"age": 8
}
]
}
1.3 JSON 與其他資料格式比較
與 XML 相比,JSON 的結構更簡潔且佔用空間更小,因而更適合現代網路傳輸需求。此外,JSON 資料的解析速度通常比 XML 快,因此在大規模資料交換場景中具有顯著優勢。儘管 YAML 也具備可讀性,但 JSON 的普及程度更高,在 API 與 Web 開發中擁有更廣泛的應用場景。
二、Python 中的 JSON 模組
Python 提供了一個強大的內建模組——json 模組,用於處理 JSON 資料的編碼(序列化)與解碼(反序列化)。通過 json 模組,我們可以輕鬆地將 Python 資料結構(例如 dict、list 等)轉換為 JSON 格式,也可以將 JSON 字串轉換回 Python 資料結構、資料類型。
2.1 Python 與 JSON 資料型態對照表
下表總結了 Python 與 JSON 之間的資料型態轉換關係:
Python 資料型態 | JSON 表示 |
---|---|
dict | object |
list、tuple | array |
str | string |
int、float | number |
True / False | true / false |
None | null |
這個對照表顯示了在轉換過程中,Python 的資料型態會如何對應到 JSON 中,反之亦然。這使得在跨語言資料交換時,可以確保資料格式的一致性。
2.2 基本函數介紹
Python json 模組主要提供以下函數:
- json.dumps():將 Python 物件轉換為 JSON 字串(序列化)。
- json.dump():將 Python 物件轉換為 JSON 格式,並寫入到檔案中。
- json.loads():將 JSON 字串轉換為 Python 物件(反序列化)。
- json.load():從檔案中讀取 JSON 文件資料,並轉換為 Python 物件。
您可以使用 json.dumps() 函數的多個參數來調整輸出的 JSON 字符串格式,包括:
- indent=None:當未設置縮進時,輸出的 JSON 字符串將緊湊地排列,沒有額外的縮進或換行。
- default=None:如果未提供自定義的序列化函數,當遇到無法序列化的對象時,json.dumps() 會引發 TypeError。
- separators=None:在未指定分隔符時,預設使用 (‘, ‘, ‘: ‘),即項目之間用逗號加空格分隔,鍵和值之間用冒號加空格分隔。
- parse_int=None:此可選參數允許您指定一個函數,該函數將被用來解析 JSON 中的整數值。預設情況下,json.loads() 使用內建的 int() 函數來解析整數。如果您希望將 JSON 中的整數解析為其他類型(例如 float),可以提供自定義的函數。
- parse_float=None:此可選參數允許您指定一個函數,該函數將被用來解析 JSON 中的浮點數值。預設情況下,json.loads() 使用內建的 float() 函數來解析浮點數。如果您希望將 JSON 中的浮點數解析為其他類型(例如 decimal.Decimal),可以提供自定義的函數。
- object_pairs_hook=None:此可選參數允許您指定一個函數,該函數將被用來處理 JSON 對象中的鍵值對列表。當解析 JSON 對象時,object_pairs_hook 會接收一個有序的鍵值對列表,並返回一個自定義的對象。這在需要保持鍵的順序或實現自定義對象時特別有用。如果同時指定了 object_hook 和 object_pairs_hook,則 object_pairs_hook 具有優先權。
- ensure_ascii:控制非 ASCII 字符的處理。設置為 True 時,所有非 ASCII 字符會被轉義;設置為 False 時,則輸出原始字符。
這些參數允許您根據需要自定義 JSON 的輸出格式。
接下來,我們將分別介紹這些函數的使用方法及實作範例。
三、JSON 與 Python 資料轉換實作
3.1 JSON 字串轉 Python 物件
方法一:使用 json.load() 讀取檔案
假設有一個名為 test.json 的 JSON 檔案,其內容如下:
{
"Student 1": {
"Student_id": 126,
"Name": "Jack",
"Score": 99,
"Friends": ["Tim", "Jen", "Ken"]
},
"Student 2": {
"Student_id": 128,
"Name": "Ken",
"Score": 99,
"Friends": ["Jack", "Jessy", "Cathy"]
}
}
使用 json.load() 直接讀取檔案內容並轉換為 Python 字典:
import json
# 讀取 JSON 檔案並轉換為 Python dict
with open('test.json', 'r') as f:
python_dict = json.load(f)
print("Python Dict:", python_dict)
print("Type:", type(python_dict))
執行結果會顯示一個 Python 字典,並印出其型別為 <class ‘dict’>。
方法二:使用 json.loads() 處理 JSON 字串
如果先將 JSON 檔案內容讀取為字串,再使用 json.loads() 將其轉換為 Python 物件:
import json
# 先讀取整個 JSON 檔案為字串
with open('test.json', 'r') as f:
json_text = f.read()
print("JSON Format:", json_text)
python_dict = json.loads(json_text)
print("Python Dict:", python_dict)
print("Type:", type(python_dict))
這兩種方法各有優點,json load 更適合直接從檔案讀取,而 json loads 則適用於需要先處理或修改字串的情境。
3.2 Python 物件轉 JSON 字串
方法一:使用 json dump 寫入檔案
假設我們有一個 Python 字典,需要將其儲存為 JSON 檔案:
import json
python_dict = {'Name': "Jack", 'Score': 99}
# 將 Python dict 寫入 JSON 檔案
with open("python2json.json", 'w') as f:
json.dump(python_dict, f)
這段程式碼會將 Python 字典轉換為 JSON 格式,並儲存到 python2json.json 檔案中。
方法二:使用 json dumps 生成 JSON 字串
如果我們希望先生成 JSON 字串再進行其他處理,例如顯示在介面上或進行網路傳輸:
import json
python_dict = {'Name': "Jack", "Score": 99}
json_str = json.dumps(python_dict)
print("JSON Format:", json_str)
print("Type:", type(json_str))
# 將 JSON 字串寫入檔案
with open('python2json1.json', 'w') as f:
f.write(json_str)
這樣可以靈活地將資料轉換為 JSON 字符串後進行儲存或傳輸。
3.3 格式化與美化 JSON 輸出
在實際專案中,儲存的 JSON 資料若排版混亂,對於後續的維護與除錯會非常不便。因此可以使用 indent 參數來進行美化格式化,使輸出結果更具可讀性。
範例一:使用 json.dumps() 的 indent 參數
import json
python_dict = {'Name': "Jack", 'Score': 99}
pretty_json = json.dumps(python_dict, indent=4)
print("Formatted JSON:", pretty_json)
# 儲存格式化後的 JSON 至檔案
with open('python2json_pretty.json', 'w') as f:
f.write(pretty_json)
範例二:使用 json.dump() 直接寫入格式化檔案
import json
python_dict = {
"Student A": {
"Student_id": 126,
"Name": "Jack",
"Score": 99,
"Friends": ["Tim", "Jen", "Cindy"]
},
"Student B": {
"Student_id": 128,
"Name": "Ken",
"Score": 98,
"Friends": ["Jack", "Jessy", "Cathy"]
}
}
with open('python2json_pretty2.json', 'w') as f:
json.dump(python_dict, f, indent=4)
設定 indent=4 表示每一層縮排 4 個空格,讓 JSON 結構清晰明瞭。
四、進階應用:自定義 JSON 編碼與解碼
在實際開發中,有時候會遇到 Python 內建資料型態無法直接序列化的情況(例如 complex 物件、日期時間物件或自定義類別)。這時可以透過自定義編碼器與解碼器來解決此問題。
4.1 自定義編碼器
可以藉由傳入 default 函數參數,或繼承 JSONEncoder 類別來擴充 JSON 的編碼功能。以下範例展示如何處理 Python 中的 complex 物件:
import json
def custom_json(obj):
if isinstance(obj, complex):
# 將 complex 物件轉換成可序列化的字典格式
return {'__complex__': True, 'real': obj.real, 'imag': obj.imag}
# 如果不是 complex,則拋出 TypeError
raise TypeError(f'Object of type {type(obj)} is not JSON serializable')
# 序列化 complex 物件
complex_obj = 2 + 3j
json_str = json.dumps(complex_obj, default=custom_json)
print("Complex JSON:", json_str)
透過自定義函數,我們可以將無法直接序列化的物件轉換成字典,再由 json 模組處理。
4.2 自定義解碼器
若 JSON 中包含自定義標記(例如上述 complex 物件轉換後的字典格式),可以在解碼時使用 object_hook 將其轉換回原始物件:
import json
def as_complex(dct):
if '__complex__' in dct:
return complex(dct['real'], dct['imag'])
return dct
# 假設從 JSON 取得的字串包含 complex 標記
json_complex = '{"__complex__": true, "real": 2, "imag": 3}'
restored_obj = json.loads(json_complex, object_hook=as_complex)
print("Restored Complex Object:", restored_obj)
print("Type:", type(restored_obj))
這樣做可以確保在反序列化時,特殊標記的物件能被正確還原。
五、使用 JSON 處理檔案 I/O
在實際應用中,經常需要將資料儲存至 JSON 檔案或從檔案中載入資料。以下分別展示如何進行這些操作。
5.1 儲存 JSON 檔案
假設我們有一筆資料需要儲存:
import json
data = {
"name": "crab",
"age": 18,
"city": "New York",
"skills": ["Python", "Machine Learning", "Data Analysis"]
}
json_path = 'data.json'
with open(json_path, "w") as json_file:
json.dump(data, json_file, indent=4)
print("JSON 資料已成功寫入檔案")
這段程式碼將 Python 字典資料轉換成 JSON 格式並以美化格式寫入檔案,方便後續檢視與維護。
5.2 讀取 JSON 檔案
同理,從檔案中載入 JSON 資料:
import json
json_path = 'data.json'
with open(json_path, "r") as json_file:
data = json.load(json_file)
print("讀取到的 JSON 資料:", data)
讀取過程中,json 模組會自動將 JSON 轉換為相應的 Python 資料型態,方便進一步處理。
六、進階主題與最佳實踐
6.1 JSON 處理中的性能考量
在大量資料處理時,JSON 的解析速度和內存消耗都是需要考慮的因素。以下是一些建議:
- 檔案分割: 當 JSON 資料量巨大時,可以將資料分割成多個檔案,或使用 JSON Lines 格式(每行一個 JSON 物件),以減少單次載入的記憶體壓力。
- 流式處理: 使用 json.load() 或 json.dump() 時,應盡量避免一次性載入超大檔案,可考慮分批讀取或使用流式處理技術。
- 錯誤處理: 當處理不可信來源的 JSON 資料時,要格外注意資安問題,避免因解析錯誤導致資源耗盡或安全漏洞。
6.2 與其他序列化工具的比較
除了 JSON,Python 還提供了 pickle、marshal 等序列化工具。各工具優缺點比較如下:
序列化工具 | 優點 | 缺點 |
---|---|---|
JSON | 可讀性高、跨語言支援、輕量 | 僅支援基本資料型態,不支援自定義類別的原生序列化 |
pickle | 能序列化大部分 Python 資料型態 | 不安全、不適用於跨語言資料交換,且版本間兼容性較差 |
marshal | 運行速度快 | 格式不穩定、僅限 Python 內部使用 |
總結來看,若需進行跨平台、跨語言的資料交換,JSON 是最佳選擇;而在單一 Python 環境內,pickle 可能會提供更靈活的序列化支援,但要特別注意安全性問題。
6.3 常見錯誤與解決方案
在使用 json 模組時,常見的錯誤包括:
- JSONDecodeError: 當 JSON 格式不正確時(例如缺少引號、逗號位置錯誤等),解析過程會引發該錯誤。解決方法:檢查 JSON 字串的格式與標點符號是否符合標準。
- TypeError: 在序列化非基本型態資料(例如自定義類別、complex 物件)時,會引發此錯誤。解決方法:透過自定義 default 函數或繼承 JSONEncoder 類別來實現轉換。
- UnicodeDecodeError: 當 JSON 檔案的編碼與預期不符時,可能會出現此錯誤。解決方法:確保檔案使用 UTF-8 或其他支援的編碼格式,或在讀取時指定正確的編碼參數。
七、命令列工具與輔助工具
7.1 使用 json.tool 命令列工具
Python 提供的 json.tool 命令列工具能夠輕鬆檢查 JSON 格式並進行美化排版。在命令列執行以下指令:
echo '{"json": "obj"}' | python -m json.tool
系統將回傳格式化後的 JSON,方便檢查格式正確性。若 JSON 資料錯誤,工具也會輸出詳細錯誤訊息,幫助使用者迅速定位問題。
7.2 整合至開發流程
在開發 API 與資料交換系統時,建議採用以下最佳實踐:
- 自動化測試: 使用單元測試檢查 JSON 轉換過程,確保每次修改後資料格式仍符合預期。
- 日誌記錄: 將 JSON 資料的讀取與寫入錯誤記錄下來,以便進行後續除錯與效能分析。
- 安全性檢查: 對於外部來源的 JSON 資料,必須設定大小限制、格式校驗,並避免過度依賴自動解析,以防範惡意攻擊(例如拒絕服務攻擊)。
關於 Python JSON 的常見問題 (FAQ)
Q1:JSON 為何在現代網路應用中如此普及?
A1:JSON 格式輕量、可讀性高且基於文本,能夠在多種程式語言中通用。這使得 JSON 成為跨平台資料交換的理想選擇,特別適合 REST API 的設計與實現。
Q2:如何選擇使用 json.load() 與 json.loads()?
A2:若資料存放在檔案中,建議使用 json.load() 直接從檔案讀取並解析;若資料已經是字串格式或需要進行預處理,則使用 json.loads() 更為合適。
Q3:如何處理 Python 中無法直接序列化的物件?
A3:可透過定義 default 函數,或者繼承 JSONEncoder 類別來處理特殊物件,例如 complex 物件、日期時間物件或自定義類別。
Q4:如何確保 JSON 輸出結果的可讀性?
A4:可以在呼叫 json.dumps() 或 json.dump() 時傳入 indent 參數,例如 indent=4,這樣會自動對每層資料進行縮排,提升輸出的可讀性。
Q5:處理不可信來源的 JSON 資料時應注意哪些安全問題?
A5:對於外部來源的 JSON 資料,應設置大小限制、進行格式校驗,並使用 try/except 處理潛在的解析錯誤,以防止因惡意資料導致系統資源耗盡或安全漏洞。
總結
本文從 JSON 的定義與基本語法開始,深入講解了 Python 中 json 模組的使用方法,包括如何進行 JSON 與 Python 資料之間的轉換、格式化輸出、自定義編碼與解碼等進階應用。同時,我們也探討了 JSON 資料處理中的性能考量、與其他序列化工具的比較,以及常見錯誤的解決方案與命令列工具的輔助應用。
資料來源
- 寫給自己的技術筆記 — 作為程式開發者我們絕對不能忽略的JSON — Python 如何處理JSON文件 | by Chwang | Medium
- json — JSON 編碼器與解碼器 — Python 3.13.2 說明文件
- Day11 – Python 如何處理 JSON – iT 邦幫忙::一起幫忙解決難題,拯救 IT 人的一天