3.2 json - 序列化
3.2 json - 序列化
JSON(JavaScript Object Notation)是現代應用程式中最常用的資料交換格式。Python 的 json 模組提供了簡單的 API 來處理 JSON 資料。
基本操作
序列化(Python 物件 → JSON 字串)
1import json
2
3# 字典轉 JSON 字串
4data = {"name": "Python", "version": 3.11}
5json_str = json.dumps(data)
6# '{"name": "Python", "version": 3.11}'
7
8# 格式化輸出
9json_str = json.dumps(data, indent=2)
10# {
11# "name": "Python",
12# "version": 3.11
13# }反序列化(JSON 字串 → Python 物件)
1import json
2
3json_str = '{"name": "Python", "version": 3.11}'
4data = json.loads(json_str)
5# {'name': 'Python', 'version': 3.11}檔案讀寫
1import json
2
3# 寫入檔案
4with open("config.json", "w", encoding="utf-8") as f:
5 json.dump(data, f, indent=2)
6
7# 讀取檔案
8with open("config.json", "r", encoding="utf-8") as f:
9 data = json.load(f)實際範例:Hook 系統
Hook 輸入讀取
來自 .claude/lib/hook_io.py:
1import json
2import sys
3
4def read_hook_input() -> dict:
5 """
6 從 stdin 讀取 Hook 輸入
7
8 Returns:
9 dict: 解析後的 JSON 資料,解析失敗時返回空字典
10 """
11 try:
12 return json.load(sys.stdin)
13 except json.JSONDecodeError:
14 return {}
15 except Exception:
16 return {}Hook 輸出寫入
1def write_hook_output(
2 output: dict,
3 ensure_ascii: bool = False,
4 indent: int = 2
5) -> None:
6 """
7 輸出 Hook 結果到 stdout
8
9 Args:
10 output: 要輸出的字典
11 ensure_ascii: 是否確保 ASCII 編碼
12 indent: JSON 縮排空格數
13 """
14 print(json.dumps(output, ensure_ascii=ensure_ascii, indent=indent))重要參數
ensure_ascii
控制是否將非 ASCII 字元轉換為跳脫序列:
1import json
2
3data = {"message": "你好"}
4
5# ensure_ascii=True(預設)
6json.dumps(data)
7# '{"message": "\\u4f60\\u597d"}'
8
9# ensure_ascii=False(保留原字元)
10json.dumps(data, ensure_ascii=False)
11# '{"message": "你好"}'在 Hook 系統中,我們使用 ensure_ascii=False 來保留中文字元。
indent
控制輸出的縮排:
1data = {"name": "Python", "features": ["simple", "readable"]}
2
3# 無縮排
4json.dumps(data)
5# '{"name": "Python", "features": ["simple", "readable"]}'
6
7# 有縮排
8json.dumps(data, indent=2)
9# {
10# "name": "Python",
11# "features": [
12# "simple",
13# "readable"
14# ]
15# }sort_keys
按鍵名排序輸出:
1data = {"z": 1, "a": 2, "m": 3}
2
3json.dumps(data, sort_keys=True)
4# '{"a": 2, "m": 3, "z": 1}'default
處理無法序列化的物件:
1from datetime import datetime
2import json
3
4def json_serializer(obj):
5 if isinstance(obj, datetime):
6 return obj.isoformat()
7 raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
8
9data = {"timestamp": datetime.now()}
10json.dumps(data, default=json_serializer)
11# '{"timestamp": "2024-01-20T15:30:00"}'型別對應
| Python 型別 | JSON 型別 |
|---|---|
| dict | object |
| list, tuple | array |
| str | string |
| int, float | number |
| True | true |
| False | false |
| None | null |
常見錯誤處理
JSONDecodeError
1import json
2
3def safe_parse_json(json_str: str) -> dict:
4 """安全解析 JSON,失敗時返回空字典"""
5 try:
6 return json.loads(json_str)
7 except json.JSONDecodeError as e:
8 print(f"JSON 解析錯誤: {e}")
9 return {}無法序列化的物件
1import json
2from dataclasses import dataclass, asdict
3
4@dataclass
5class Config:
6 name: str
7 timeout: int
8
9config = Config("test", 30)
10
11# 錯誤:dataclass 無法直接序列化
12# json.dumps(config) # TypeError
13
14# 正確:轉換為字典
15json.dumps(asdict(config))
16# '{"name": "test", "timeout": 30}'實際應用:配置檔案載入
來自 .claude/lib/config_loader.py:
1def _load_json_file(file_path: Path) -> dict:
2 """載入 JSON 檔案"""
3 import json
4 with open(file_path, "r", encoding="utf-8") as f:
5 return json.load(f)與 YAML 的比較
Hook 系統同時支援 JSON 和 YAML:
1# 嘗試導入 PyYAML,如果失敗則使用 JSON 作為備案
2try:
3 import yaml
4 HAS_YAML = True
5except ImportError:
6 HAS_YAML = False
7 import json
8
9def load_config(config_name: str) -> dict:
10 if yaml_path.exists() and HAS_YAML:
11 return _load_yaml_file(yaml_path)
12 elif json_path.exists():
13 return _load_json_file(json_path)最佳實踐
1. 總是指定編碼
1# 好
2with open("data.json", "w", encoding="utf-8") as f:
3 json.dump(data, f)
4
5# 不好(可能在不同系統有不同行為)
6with open("data.json", "w") as f:
7 json.dump(data, f)2. 處理解析錯誤
1def read_config(path: str) -> dict:
2 try:
3 with open(path, "r", encoding="utf-8") as f:
4 return json.load(f)
5 except FileNotFoundError:
6 return {}
7 except json.JSONDecodeError:
8 return {}3. 使用 ensure_ascii=False 處理中文
1# 輸出中文友好的 JSON
2json.dumps(data, ensure_ascii=False, indent=2)思考題
json.dump()和json.dumps()有什麼區別?- 為什麼 Hook 系統的
read_hook_input()捕獲JSONDecodeError後返回空字典而不是拋出異常? - 如何將包含
datetime物件的字典序列化為 JSON?
實作練習
- 寫一個函式,合併多個 JSON 檔案
- 實作一個支援註解的 JSON 讀取器(移除
//開頭的行) - 寫一個函式,比較兩個 JSON 檔案的差異
上一章:pathlib - 路徑操作 下一章:subprocess - 執行外部命令