Python JSON實戰技巧:高效處理 API 與設定檔,不再踩坑

Python JSON實戰技巧:高效處理 API 與設定檔,不再踩坑

在當今以數據為核心的開發環境中,無論是串接網頁應用程式介面(REST API)、管理設定檔,還是進行程式間的資料交換,JSON (JavaScript Object Notation) 都扮演著舉足輕重的角色。它是一種輕量級、易於人類閱讀和編寫,同時也易於機器解析和生成的資料交換格式。其純文字的本質和結構化的特性,使其成為跨語言、跨平臺資料溝通的首選標準。

Python 憑藉其強大的生態系和簡潔的語法,內建了功能完整的 json 套件,讓我們可以輕鬆地對 JSON 資料進行序列化(Serialization)和反序列化(Deserialization)操作。本篇文章將從基礎概念出發,深入探討 Python json 套件的核心功能與進階技巧,幫助您完全掌握處理 JSON 資料的能力。

JSON 基礎與 Python 資料型態的對應關係

在操作之前,我們必須先理解 JSON 的基本結構以及它如何對應到 Python 的資料類型。JSON 資料由兩種基本結構組成:

  1. 物件 (Object):由大括號 {} 包圍的鍵值對 (key-value pair) 集合。鍵必須是字串,並使用雙引號 “” 包覆;值可以是string、數字、布林值、array或另一個物件。這在 Python 中直接對應到 字典 (dict)。
  2. 陣列 (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 資料物件。

資料來源

返回頂端