在 Python 中,你可以使用內建的 xml.etree.ElementTree 模組搭配一個小型遞迴輔助函式來將 XML 轉換為 JSON,也可以用第三方的 xmltodict 函式庫一行搞定;如果你完全不想寫程式,瀏覽器版的 XML 轉 JSON 轉換器可以在本機完成同樣的轉換,無需安裝任何軟體。標準函式庫的做法讓你對輸出的結構擁有完整掌控權,第三方函式庫讓你享有速度,而瀏覽器工具則提供一個不需要上傳的選項,對於 API 範例、設定檔檢視以及測試固件特別有用。這三種方式都能產生有效的 JSON,但不會產生完全相同的 JSON,因為 XML 與 JSON 之間並沒有一個通用的對應關係。

Python 內建方案:xml.etree.ElementTree 搭配 json
標準函式庫已內建解析 XML 與輸出 JSON 所需的一切,這使得 xml.etree.ElementTree 成為無法安裝第三方套件時的預設選擇。ElementTree.parse() 會回傳一棵由 Element 物件組成的樹,其中 element.tag 存放本地標籤名稱,element.text 存放直接文字內容,而 element.attrib 則是一個包含屬性的字典。由於這棵樹並沒有自動對應到 JSON 的機制,你必須撰寫一個小型遞迴輔助函式,將每個 Element 轉換成普通的 dict,再把結果交給 json.dumps()。
你所輸出的結構完全由你決定。一個常見的慣例是將屬性包進一個 @attributes 鍵底下,當元素同時擁有子元素或屬性時,文字內容則放在 #text 鍵底下,而重複的同層級標籤則會被轉為 JSON 陣列。標準函式庫並不強制規定這些規則,這也是為什麼兩份寫得很正確的 Python 腳本可能從同一份 XML 產生截然不同的 JSON。這種自由度對於有嚴格需求的消費者所運行的正式 ETL 流程很有幫助,但也代表你必須自己負責對應規則並進行測試。
第三方捷徑:xmltodict
如果你不想自己撰寫遞迴邏輯,xmltodict 是被引用最廣泛、單行就能在 Python 中將 XML 轉換為 JSON 的做法。在執行 pip install xmltodict 之後,只需要兩行程式碼即可完成轉換:xmltodict.parse(xml_string) 會回傳一個反映文件結構層次的 OrderedDict,接著用 json.dumps(dict_data, indent=2) 將其序列化。
xmltodict 預設會使用 @ 前綴來表示屬性(例如一個 id 屬性會變成 @id),並在字元資料與屬性或子元素並存時使用 #text 鍵來存放。重複的同層級元素會自動變成清單。這些預設行為雖然方便,卻也把你鎖定在某一種特定的輸出形狀中,可能與你的消費者所期待的結構並不相符。若需要更嚴謹的下游契約,你可以在轉換完成後,根據一份 JSON 範例在 Python 中產生 JSON Schema,並用它來進行驗證。
如何在不寫 Python 的情況下將 XML 轉換為 JSON
當目標只是快速檢視或分享單一 XML 文件時,安裝 Python 然後寫程式就顯得殺雞用牛刀了。XML 轉 JSON 轉換器接受一份格式正確的 XML 文件,在瀏覽器中透過 DOMParser API 在本機進行解析,並套用工具頁面所記載的明確對應規則。整個過程不會上傳任何文件,也不會發出任何網路請求,這對於不想離開本機的設定檔和 API 回應來說非常重要。
- 將一份格式正確的 XML 文件貼入輸入區域。如果你的文件開頭帶有 <!DOCTYPE ...> 宣告,請先將那一行刪除——轉換器刻意拒絕 DOCTYPE,以免被誘騙去擷取外部實體、DTD 或遠端資源。
- 選擇縮排樣式:緊湊(無空白)、兩個空格或四個空格。這個選擇只影響 JSON 的序列化方式,並不會改變其底層結構。
- 選擇「Convert to JSON」。瀏覽器會依照 XML 1.0 標準規則集來解析文件。如果解析失敗,你會看到明確的解析錯誤訊息,而不是部分輸出。
- 使用下方說明的 @attributes 與 #text 慣例檢視結果,再從輸出面板複製結果。
轉換器每次貼上最多接受 200,000 個字元,對於大多數 API 範例、設定檔以及小型資料遷移作業來說已經足夠。更大型的文件應該加以切割,或在受版本控制的應用程式中使用串流解析器來處理,而非單靠一次貼上操作。
轉換器如何決定輸出的結構
XML 轉 JSON 轉換器採用一套明確、有文件記載的對應規則,並未聲稱遵循任何官方轉換標準——因為 XML 與 JSON 之間根本不存在這樣的通用標準。了解這些規則後,輸出結果就會變得可預期。
- 根元素 會成為最上層的單一鍵。
- 屬性 會被集中放在其父元素下的 @attributes 物件中。它們絕不會以裸字串鍵的形式,與子元素並列在同一層。
- 純文字元素 會成為 JSON 字串。只有當元素同時擁有屬性或子元素時,文字才會存放在 #text 底下。
- 重複的同層級名稱 會按照文件中出現的順序變成 JSON 陣列。單一子元素則保持為單一值。
- 命名空間前綴 會在元素與屬性名稱中予以保留(例如 soap:Body 在輸出中仍維持 soap:Body)。
- CDATA 區段 會將其文字內容貢獻給 #text 或元素字串。
- 註解與處理指令 不會被複製到 JSON 輸出中。
- 混合內容 會將直接文字放在 #text 底下,並以名稱區分子元素,但文字與子元素之間的精確交錯順序不會被表示出來。
沒有任何值會被轉換為數字、布林值或 null 了。這是有意為之:詞法上的 XML 文字(例如 007 或 true)在下游消費 JSON 時不應被靜悄悄地改變其含義。如果你的應用程式需要具型別的值,請在轉換後自行強制轉型。
比較 Python 輸出與轉換器輸出
想看出不同做法之間的差異,最清楚的方式就是看一小段 XML 片段與三種可能的 JSON 結果。假設有一份文件,其中包含一個訂單,帶有兩個屬性、兩個重複的 item 子元素,以及一個 note:
| 元素或屬性 | Python xmltodict | 瀏覽器轉換器 |
|---|---|---|
| 根元素 | 最上層鍵 order | 最上層鍵 order |
| 屬性 | 使用 @ 前綴扁平化,例如 @id | 集中放在 @attributes 物件下 |
| 重複的子元素 | 依來源順序組成 JSON 陣列 | 依來源順序組成 JSON 陣列 |
| 與子元素並存的文字 | 存放在 #text 底下 | 存放在 #text 底下 |
| 命名空間前綴 | 保留 | 保留 |
使用 xmltodict 執行後,會產生一個形如 {"order": {"@id": "42", "@status": "paid", "item": ["Book", "Pen"], "note": "Gift wrap"}} 的 OrderedDict。而瀏覽器轉換器則會產生相同的結構輪廓,但會把屬性搬移到 @attributes 之中:{"order": {"@attributes": {"id": "42", "status": "paid"}, "item": ["Book", "Pen"], "note": "Gift wrap"}}。這個差異雖然小,卻是真實存在的,這就是為什麼你絕不應在沒有契約檢查的情況下,把某個工具的輸出直接串接到期待另一個工具結構的消費者。
在 Python 與瀏覽器工具之間做選擇
當你需要自訂對應規則、以腳本批次處理大量檔案、需要串流處理,或是轉換必須納入自動化管線時,就選擇 Python。當你眼前只有單一文件、希望擁有固定且易於檢視的結構,且不想撰寫或除錯遞迴輔助函式時,就選擇瀏覽器轉換器。這兩種做法各自覆蓋了同一個工作流程中的不同環節,而不是互相競爭的方案。
無論選擇哪一種,都請先確認你的應用程式所要求的輸出契約。別的函式庫可能一律為子元素輸出陣列、保留註解、強制轉型數字與布林值,或將命名空間拆成獨立的物件——這些差異會在不知不覺間毀掉一個期待你所選工具或函式庫所產生結構的消費者。