Python處理JSON的完全指南:從基礎到實戰(zhàn),掌握數(shù)據交換核心技能
環(huán)境:Python 3.x | 適用人群:Python 初學者、需要處理 API 數(shù)據或配置文件的開發(fā)者
你是不是也遇到過這些問題?從 API 接口拿到一串 JSON 數(shù)據,卻不知道如何高效地解析和提取信息;想把 Python 對象保存到文件,卻發(fā)現(xiàn)直接序列化會報錯;或者寫配置文件時,總在逗號、引號這些語法細節(jié)上栽跟頭?JSON 作為現(xiàn)代數(shù)據交換的“普通話”,是每個 Python 開發(fā)者必須掌握的核心技能。
本文將從 JSON 與 Python 的類型映射講起,帶你理解序列化與反序列化的底層邏輯,然后通過實戰(zhàn)案例演示如何解析 API 數(shù)據、處理自定義對象,最后總結出 3 個避坑技巧。讀完本文,你不僅能掌握 json 模塊的所有核心方法,還能獲得一套可直接復用的代碼模板,輕松應對日常開發(fā)中的 JSON 處理需求。
一、JSON 與 Python:類型映射全解析
JSON(JavaScript Object Notation)是一種輕量級的數(shù)據交換格式,Python 的 json 模塊提供了兩者間的雙向轉換能力。理解它們的類型對應關系是避免后續(xù)錯誤的基礎。
| JSON 類型 | Python 類型 | 示例與注意事項 |
|---|---|---|
object | dict | {"name": "張三"} → {"name": "張三"} |
array | list 或 tuple | [1, 2, 3] → [1, 2, 3] |
string | str | "hello" → "hello" |
number | int 或 float | 42 → 42, 3.14 → 3.14 |
true/false | True/False | 注意 Python 首字母大寫 |
null | None | JSON 的 null 對應 Python 的 None |
關鍵區(qū)別:JSON 的鍵必須是字符串(雙引號包裹),而 Python 字典的鍵可以是任何不可變類型(字符串、數(shù)字、元組)。這是序列化時最常見的類型錯誤來源之一。
很多人以為 json.dumps() 會把 Python 的 tuple 也轉成 JSON 數(shù)組,這沒錯。但一個隱藏的細節(jié)是:當 JSON 數(shù)組被 json.loads() 反序列化時,它總是變成 Python 的 list,而不是 tuple。如果你需要元組的不可變性,需要在反序列化后手動轉換。
二、Python 到 JSON:序列化實戰(zhàn)與避坑
序列化(Serialization)是將 Python 對象轉換為 JSON 字符串的過程,主要通過 json.dumps()(輸出字符串)和 json.dump()(寫入文件)實現(xiàn)。
2.1 基礎序列化:從字典到 JSON 字符串
下面的代碼演示如何將一個包含嵌套結構的 Python 字典轉換為格式化的 JSON 字符串:
import json
# 準備要序列化的數(shù)據
data = {
"name": "username",
"age": 18,
"is_teacher": False,
"id": "202205002626",
"address": {
"city": "xi an",
"town": "bigtown"
}
}
# 基礎轉換
json_str = json.dumps(data)
print(f"基礎轉換結果:{json_str}")
print(f"類型:{type(json_str)}")
運行結果:
基礎轉換結果:{"name": "username", "age": 18, "is_teacher": false, "id": "202205002626", "address": {"city": "xi an", "town": "bigtown"}}
類型:<class 'str'>
Python 內部機制:json.dumps() 在底層會遞歸遍歷 Python 對象,根據上述類型映射表進行轉換。對于 bool 類型,Python 的 True/False 會被轉換為 JSON 的 true/false(小寫)。這個過程在 CPython 中由 C 語言編寫的掃描器完成,速度很快。
2.2 美化輸出:讓 JSON 更易讀
實際開發(fā)中,我們經常需要查看或調試 JSON 數(shù)據,indent 和 ensure_ascii 參數(shù)能顯著提升可讀性:
# 美化輸出:縮進、中文支持、鍵排序
json_pretty = json.dumps(
data,
indent=2, # 縮進2個空格
ensure_ascii=False, # 中文不轉義為 \uXXXX
sort_keys=True # 按鍵名字母順序排序
)
print("美化后的 JSON:")
print(json_pretty)
運行結果:
美化后的 JSON:
{
"address": {
"city": "xi an",
"town": "bigtown"
},
"age": 18,
"id": "202205002626",
"is_teacher": false,
"name": "username"
}
2.3 寫入文件:持久化存儲配置
將數(shù)據保存到文件是配置管理、數(shù)據緩存的常見場景:
# 將數(shù)據寫入 JSON 文件
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
print("數(shù)據已成功寫入 data.json 文件")
# 驗證文件內容
with open("data.json", "r", encoding="utf-8") as f:
content = f.read()
print("文件內容預覽:")
print(content[:100] + "..." if len(content) > 100 else content)
注意:ensure_ascii=False 參數(shù)允許中文字符直接保存,而不是轉義為 \uXXXX 格式。但某些舊系統(tǒng)或嚴格遵循 RFC 8259 的解析器可能要求純 ASCII,請根據實際環(huán)境選擇。寫入文件時指定 encoding='utf-8' 是跨平臺的最佳實踐。
2.4 我踩過的坑:語法錯誤全解析
很多初學者(包括曾經的我)在寫 JSON 或 Python 字典時容易犯以下錯誤:
錯誤1:使用 = 而不是 :
# ? 錯誤寫法
data = {"name" = "username"} # SyntaxError
# ? 正確寫法
data = {"name": "username"}
錯誤2:鍵值對之間缺少逗號
# ? 錯誤寫法
data = {
"name": "username"
"age": 18 # 這里缺少逗號!
}
# ? 正確寫法
data = {
"name": "username",
"age": 18 # 每個鍵值對后必須有逗號(最后一個可選)
}
錯誤3:使用中文標點
# ? 錯誤寫法(中文逗號)
data = {"name": "username", "age": 18} # JSONDecodeError
# ? 正確寫法(英文逗號)
data = {"name": "username", "age": 18}
排查技巧:當遇到 JSONDecodeError 時,Python 會告訴你出錯的行和列。仔細檢查該位置附近的標點符號,90% 的問題都是逗號、引號或冒號使用不當。
三、JSON 到 Python:反序列化實戰(zhàn)
反序列化(Deserialization)是將 JSON 字符串轉換回 Python 對象的過程,主要通過 json.loads()(從字符串)和 json.load()(從文件)實現(xiàn)。
3.1 從字符串解析:處理 API 響應
假設你從某個 API 接口收到了以下 JSON 字符串:
import json
# 模擬 API 返回的 JSON 字符串
json_str = """{
"name": "username",
"age": 18,
"id": "2022552203"
}"""
print(f"原始字符串:{json_str}")
print(f"字符串類型:{type(json_str)}")
# 反序列化
python_obj = json.loads(json_str)
print(f"\n反序列化結果:{python_obj}")
print(f"結果類型:{type(python_obj)}")
print(f"訪問字段 - 姓名:{python_obj['name']}")
運行結果:
原始字符串:{
"name": "username",
"age": 18,
"id": "2022552203"
}
字符串類型:<class 'str'>
反序列化結果:{'name': 'username', 'age': 18, 'id': '2022552203'}
結果類型:<class 'dict'>
訪問字段 - 姓名:username
3.2 從文件讀?。杭虞d配置文件
更常見的場景是從 JSON 配置文件中讀取數(shù)據:
# 從之前創(chuàng)建的 data.json 文件讀取
with open("data.json", "r", encoding="utf-8") as f:
loaded_data = json.load(f) # 注意是 load() 不是 loads()
print(f"從文件加載的數(shù)據:{loaded_data}")
print(f"數(shù)據類型:{type(loaded_data)}")
print(f"嵌套訪問 - 城市:{loaded_data['address']['city']}")
3.3 常見反序列化錯誤及解決
錯誤示例1:JSON 語法錯誤
# ? 錯誤寫法:鍵未加雙引號
invalid_json = """{
name: "username", # 鍵應該用雙引號
"age": 18
}"""
# ? 錯誤寫法:使用中文逗號
invalid_json2 = """{
"name": "username",
"age": 18
}"""
# ? 正確寫法
valid_json = """{
"name": "username",
"age": 18
}"""
錯誤示例2:類型不匹配與數(shù)字精度
# JSON 中的數(shù)字可能超出 Python int 范圍
big_num_json = '{"id": 999999999999999999999999999999}'
try:
result = json.loads(big_num_json)
print(f"大數(shù)字處理:{result['id']}") # Python 會自動轉為 int
except Exception as e:
print(f"錯誤:{e}")
# 浮點數(shù)精度問題
float_json = '{"price": 0.1 + 0.2}' # 這本身是無效JSON,僅作示意
# 實際中,0.1+0.2在JSON中就是0.3,但Python計算0.1+0.2 != 0.3
安全提醒:從不可信來源(如用戶輸入、第三方 API)加載 JSON 時,考慮使用 json.loads() 的 object_hook 參數(shù)進行驗證和過濾,或使用 demjson 等更嚴格的解析器。避免直接 eval() JSON 字符串,以防 JSON 注入攻擊。
四、處理自定義對象:進階序列化技巧
默認情況下,json 模塊無法序列化自定義類的實例。但通過兩種方式可以解決這個問題。
4.1 方法一:自定義編碼器(繼承 JSONEncoder)
這種方法適合需要多次序列化同一類對象的場景,代碼更結構化。
import json
class Person:
"""人員信息類"""
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Person(name={self.name}, age={self.age})"
class PersonEncoder(json.JSONEncoder):
"""自定義 Person 類的 JSON 編碼器"""
def default(self, obj):
# 如果是 Person 實例,轉換為字典
if isinstance(obj, Person):
return {
"_type": "Person", # 添加類型標記,便于反序列化識別
"name": obj.name,
"age": obj.age
}
# 其他類型交給父類處理(會拋出 TypeError)
return super().default(obj)
# 創(chuàng)建 Person 實例
person = Person("張三", 25)
# 使用自定義編碼器序列化
json_str1 = json.dumps(person, cls=PersonEncoder, ensure_ascii=False)
print(f"方法一結果:{json_str1}")
4.2 方法二:使用 default 回調函數(shù)
這種方法更靈活,適合臨時處理或多種類型的序列化。
def person_to_dict(obj):
"""將 Person 對象轉換為字典的回調函數(shù)"""
if isinstance(obj, Person):
return {
"_type": "Person",
"name": obj.name,
"age": obj.age
}
# 如果不是 Person 類型,拋出 TypeError
raise TypeError(f"對象類型 {type(obj)} 不可序列化")
# 使用 default 參數(shù)序列化
json_str2 = json.dumps(person, default=person_to_dict, ensure_ascii=False)
print(f"方法二結果:{json_str2}")
兩種方法的輸出相同:
{"_type": "Person", "name": "張三", "age": 25}
4.3 反序列化自定義對象
序列化只是第一步,我們還需要能將 JSON 恢復為原來的對象:
def dict_to_person(dct):
"""將字典轉換回 Person 對象的回調函數(shù)"""
if dct.get("_type") == "Person":
# 根據字典中的信息重建 Person 對象
return Person(dct["name"], dct["age"])
# 如果不是 Person 類型,返回原字典
return dct
# 反序列化:使用 object_hook 參數(shù)
person_json = '{"_type": "Person", "name": "李四", "age": 30}'
restored_person = json.loads(person_json, object_hook=dict_to_person)
print(f"反序列化結果:{restored_person}")
print(f"類型:{type(restored_person)}")
print(f"訪問屬性 - 姓名:{restored_person.name}")
運行結果:
反序列化結果:Person(name=李四, age=30)
類型:<class '__main__.Person'>
訪問屬性 - 姓名:李四
底層原理:object_hook 參數(shù)會在解析每個字典時被調用。如果返回非字典值,則該值會替換原來的字典。這使得我們可以在解析過程中“攔截”特定格式的字典,將其轉換為自定義對象。cls 參數(shù)同理,用于自定義解碼器。
下面用 Mermaid 圖展示自定義對象序列化與反序列化的完整流程:

我第一次實現(xiàn)自定義序列化時踩過一個坑:在 default 方法里沒有處理好繼承關系。當我嘗試序列化一個 Person 的子類 Student 時,isinstance(obj, Person) 判斷為 True,序列化成功了,但反序列化時 object_hook 只認 Person,丟失了子類的特有屬性。解決方案是在字典里也保存類的完整名稱(obj.__class__.__name__),并在 object_hook 里 根據類名動態(tài)創(chuàng)建對象。
五、實戰(zhàn)項目:解析 API 返回的訂單數(shù)據
現(xiàn)在我們來完成一個完整的實戰(zhàn)項目:解析一個模擬的電商 API 返回的訂單數(shù)據。
5.1 準備測試數(shù)據
首先創(chuàng)建 pra_json.json 文件,內容如下:
{
"status": 1,
"message": "success",
"result": {
"userInfo": {
"uid": 5689,
"username": "study_json",
"createTime": "2025-08-12"
},
"order": [
{
"orderNo": "OD20250905001",
"payMoney": 258.6,
"product": [
{"pId": 221, "pName": "Python實戰(zhàn)書籍"},
{"pId": 222, "pName": "編程配套課程"}
]
},
{
"orderNo": "OD20250905002",
"payMoney": 99,
"product": [
{"pId": 315, "pName": "JSON入門手冊"}
]
}
]
}
}5.2 完整解析代碼
下面的代碼演示了如何穩(wěn)健地解析這個復雜的嵌套 JSON 結構:
import json
def parse_order_data(file_path):
"""
解析訂單數(shù)據文件
Args:
file_path: JSON 文件路徑
Returns:
dict: 解析后的數(shù)據,包含用戶信息和訂單列表
"""
try:
# 1. 讀取并解析 JSON 文件
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f) # 關鍵:使用 load() 不是 dumps()
# 2. 檢查接口狀態(tài)
if data.get("status") != 1:
print(f"API 請求失敗: {data.get('message', '未知錯誤')}")
return None
print("? 數(shù)據獲取成功")
# 3. 提取用戶信息
user_info = data["result"]["userInfo"]
print(f"\n?? 用戶信息:")
print(f" 用戶ID: {user_info['uid']}")
print(f" 用戶名: {user_info['username']}")
print(f" 注冊時間: {user_info['createTime']}")
# 4. 提取并處理訂單信息
orders = data["result"]["order"]
total_amount = 0!")
運行結果:
以上就是Python處理JSON的完全指南:從基礎到實戰(zhàn),掌握數(shù)據交換核心技能的詳細內容,更多關于Python處理JSON的資料請關注腳本之家其它相關文章!
相關文章
Python精確統(tǒng)計函數(shù)執(zhí)行時間的多種方法
在軟件開發(fā)中,我們常常需要了解代碼的執(zhí)行效率,本文將介紹 Python中統(tǒng)計 函數(shù)執(zhí)行時間 的多種方法,及其適用場景和注意事項,需要的朋友可以參考下2026-01-01
Python基于PyGraphics包實現(xiàn)圖片截取功能的方法
這篇文章主要介紹了Python基于PyGraphics包實現(xiàn)圖片截取功能的方法,涉及PyGraphics包的安裝及基于PyGraphics包實現(xiàn)圖片的打開、讀取、截取等相關操作技巧,需要的朋友可以參考下2017-12-12
解決webdriver.Chrome()報錯:Message:''chromedriver'' executable n
這篇文章主要介紹了解決webdriver.Chrome()報錯:Message:'chromedriver' executable needs to be in Path ,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06
Python中列表(List) 的三種遍歷(序號和值)方法小結
這篇文章主要介紹了Python中列表(List) 的三種遍歷(序號和值)方法小結,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
Python+OpenCV實現(xiàn)邊緣檢測與角點檢測詳解
這篇文章主要為大家詳細介紹了如何通過Python+OpenCV實現(xiàn)邊緣檢測與角點檢測,文中的示例代碼講解詳細,對我們學習Python與OpenCV有一定的幫助,需要的可以參考一下2023-02-02

