XML 格式化工具

在你的瀏覽器中格式化,不會傳送到任何地方。文字與元素混合的內容會保持在同一行,因為在文字周圍加上換行會改變文件的意思。屬性順序與引號都會保留,也不會擅自加上 XML 宣告。

您輸入的內容不會被儲存、記錄或分享。

貼上 XML 即可將它縮排成易讀的樣子或壓成一行,同時檢查是否格式正確。錯誤會指出確切的行與列。所有處理都在你的瀏覽器中完成,不會上傳任何內容。

使用方式

  1. 將 XML 貼進輸入欄位。
  2. 選擇「格式化」或「壓縮」,需要的話設定縮排寬度。
  3. 複製結果,或查看錯誤所在的行與列。

格式正確不等於有效

XML 有兩種不同層次的「正確」,而且經常被混為一談。格式正確(well-formed)指的是文件符合語法:只有一個根元素、每個標籤都有對應的結束標籤、屬性值加上引號、註解與 CDATA 都有結束、根元素之外沒有多餘文字。這只需要文件本身就能檢查,也正是這個工具所回報的內容。

有效(valid)則表示文件還符合某個結構描述,例如 DTD、XSD 或 RelaxNG。結構描述規定了哪些元素可以出現、以什麼順序、出現幾次,以及屬性值應該長什麼樣子。沒有結構描述就不可能做這項檢查,而一份文件完全可以格式正確,卻與它宣稱遵循的文法完全不符。

命名空間則與兩者都略有不同。使用了前綴卻沒有對應的 xmlns 宣告,屬於命名空間錯誤,而不是格式錯誤。所以文件可能通過語法檢查,卻仍被支援命名空間的解析器拒絕。如果 SOAP 或 Office 文件能正常格式化卻遭到拒絕,建議先檢查是否有未宣告的前綴。

空白有時候就是資料

這是主導格式化工具所有行為的規則。元素之間的空白幾乎都只是排版,換成別的排版不會改變任何東西。但在混合內容裡(文字與元素並列,例如段落中夾著一個粗體字),空白是文字的一部分,重新排版就會改變文件本身。

因此,含有混合內容的元素會完全保留原樣,即使這會在縮排整齊的文件中留下一行很長的內容。大多數格式化工具的做法相反:全部縮排,在 Hello 與 <b>there</b> 之間插入換行,結果顯示時多出一段原本不存在的空隙。看起來比較整齊,但結果是錯的。

規格提供了 xml:space="preserve" 來明確宣告這一點,任何文字內容有意義的元素都值得加上。要注意它會被子孫元素繼承,直到被覆寫為止;只在根元素設定一次就以為沒事的人,常常會因此感到意外。

真正讓 XML 壞掉的原因

最常見的遠遠是未跳脫的「&」符號。在 XML 中,& 代表實體參照的開頭,所以網址查詢字串或公司名稱裡直接出現的 &,會讓文件格式不正確。它必須寫成 &amp;,文字中的小於符號也必須寫成 &lt;。預先定義的實體只有 amp、lt、gt、quot、apos 五個,其他都必須先宣告,或改用數值字元參照。

其次是編碼不一致。檔案開頭的宣告說明了位元組使用的編碼,如果檔案之後被另存成別的編碼,解析器會把多位元組字元讀成亂碼,或直接失敗。宣告之前的 BOM 也會讓部分解析器出錯。相關的一點是:XML 1.0 完全不允許少數控制字元出現,即使跳脫也不行。混進文字欄位的二進位資料無法靠跳脫救回,只能刪除或改用 Base64 編碼。

再來是 HTML 的習慣。沒有引號的屬性值、像 <br> 這樣沒有結束的標籤、重複的屬性,在 HTML 裡都沒問題,在 XML 裡卻是致命錯誤。如果文件來自 HTML,應該先檢查這幾點。

實體,以及解析不受信任 XML 的注意事項

XML 允許文件在 DTD 中宣告自己的實體,包括指向檔案路徑或網址的外部實體。這是兩類知名攻擊的基礎,即使你只是讀取 XML、從不撰寫,也值得了解。

XML 外部實體注入(XXE)是宣告一個指向本機檔案的實體,讓伺服器讀取該檔案並把內容放進回應裡。過去這是從任何以預設設定解析上傳 XML 的系統中竊取設定檔與帳密的可靠手法。Billion Laughs 攻擊則把實體層層巢狀,讓每一層展開成好幾個下一層,幾 KB 的輸入在記憶體中膨脹成數 GB。

兩者的防禦方式都是在解析器中停用 DTD 處理與外部實體解析。現代主流函式庫大多已預設為安全設定,但仍有不少舊程式碼沒有。本頁不會解析任何外部內容:它只把文件當作文字讀取,從不擷取文件所參照的任何東西。

命名空間:前綴不是名字

命名空間把前綴繫結到一個 URI,真正識別元素的是 URI,前綴只是可以隨意改名的本機簡寫。兩份文件分別用 soap: 和 s: 指向同一個 URI,它們是等價的;兩份文件都用 ns1: 卻指向不同的 URI,它們就不是。以前綴而非 URI 來比對的程式,會一路正常運作,直到有人重新整理了那份文件。

用 xmlns 宣告的預設命名空間只適用於元素,不適用於屬性,這是最多人搞錯的一條規則。沒有前綴的屬性不屬於任何命名空間,無論外層元素宣告了什麼都一樣,這也是 xsi:type 和 xml:lang 永遠都要寫前綴的原因。

當剖析器拒絕一份看起來格式正確的文件時,通常是因為某個前綴沒有宣告。格式化本身會成功,因為前綴繫結屬於命名空間規則而不是語法規則,問題會延後到處理這份文件的程式那裡才浮現。

結構描述驗證到底給了你什麼

格式正確談的是語法,有效談的是是否符合某套文法。DTD 是最早的機制,能力有限,但至今仍用來宣告實體。XSD 是主流,它本身就用 XML 撰寫,對資料型別的表達力很強,而且冗長到讀得懂一份 XSD 本身就是一項技能。RelaxNG 寫起來舒服得多,但支援度較低,這就是常見的取捨。

結構描述換來的是一份可以在邊界上機械化檢查的契約,在你的程式看到資料之前就先檢查:必要元素是否存在、該放數字的地方是不是數字、列舉值有沒有落在允許的集合內。在與合作夥伴的系統整合裡,結構描述往往是雙方唯一有共識的規格文件。

在流程中驗證只需要一道指令——xmllint --schema schema.xsd file.xml --noout——錯誤訊息會直接指出行號和元素。要在邊界而不是在應用程式碼裡做這件事,理由是結構描述驗證失敗會是一次乾淨而且說得出原因的拒絕,而同樣的問題到了第三層才發現,就只剩下一份堆疊追蹤。

用 XPath 把資料取出來

XPath 是 XML 的查詢語言,值得任何人花一個小時學會。//book/title 會選出文件中任何位置 book 底下的每個 title 元素;//book[@id='b1'] 依屬性選取;//book[price>30]/title 則依子元素的值過濾。它是 XSLT、Java 的 XML 工具、Python 的 lxml,以及大多數瀏覽器自動化工具底下共用的定址語言。

命名空間是 XPath 開始變麻煩的地方。運算式必須自行繫結它用到的前綴,而在有預設命名空間的文件裡,元素雖然看起來沒有前綴,實際上仍屬於那個命名空間——這就是 //book 什麼都比對不到、每個人第一次對 SOAP 回應寫 XPath 都得到空結果的原因。各家工具讓你註冊繫結的方式不一,而 local-name() 函式就是那個粗暴的逃生口。

建構在它之上的轉換語言是 XSLT,直到今天,要把一種 XML 詞彙轉成另一種、或轉成 HTML 而不必寫程序式程式碼,它仍是最實用的辦法。它難學的名聲,多半來自函數式的思維方式,而不是語法本身。

XML 依然無所不在

Office 文件就是 XML:一個 .docx 或 .xlsx 其實是壓縮檔,裡面裝著一整個目錄的 XML 組件,所以損毀的文件有時候解壓縮後修好其中一個檔案就能救回來。SVG 也是 XML,因此圖示可以用文字編輯器修改,也可以用樣板產生。RSS 與 Atom feed 是 XML,本站發布的 sitemap 同樣是 XML。

在企業與政府的系統整合裡,SOAP 遠遠稱不上已死:銀行、保險、透過 HL7 的醫療,以及多數國家的海關與稅務系統,都跑在帶有結構描述、而且經常經過簽章的 XML 上。XML Signature 與 XML Encryption 讓你可以只對大文件中的單一元素簽章,這件事 JSON 沒有對應的標準做法。

Android 的版面配置、Maven 的建置設定、.NET 的組態,以及數不清的 CI 描述檔也都是 XML。這個格式是退流行,不是被淘汰,能輕鬆讀懂它是一項耐用的能力。

剖析不是你寫的 XML

許多 XML 函式庫的預設設定對不可信的輸入並不安全,原因出在實體機制。外部實體讓一份文件可以引用本機檔案或某個網址,因此用預設設定剖析上傳 XML 的伺服器,可能被誘導去讀取 /etc/passwd 並把內容回傳——這就是 XXE 攻擊,在它被記錄下來多年之後,仍不時出現在正式環境的系統裡。

另一面則是展開。billion laughs 攻擊把實體層層巢狀,讓幾 KB 的輸入在記憶體中膨脹成好幾 GB,直接把行程打垮。這兩種攻擊都不需要什麼特別的東西:它們都是使用規格所定義之功能的普通 XML。

在大多數函式庫裡,防禦就是一行程式——關閉 DTD 處理與外部實體解析——而且值得實際確認而不是假設沒問題,因為預設值會隨語言與版本而不同。本頁完全不解析任何外部資源:它只把文件當成文字讀取,絕不去抓取文件所引用的任何東西。

常見問題

為什麼我的部分 XML 還是留在同一行?
因為那是混合內容——文字與元素並存,例如「Hello <b>there</b>.」。替它縮排會把換行與空白插進文字本身,畫面上就會顯示成「Hello there .」,中間多出一個空隙。因此這些元素會照原樣保留。多數 XML 格式化工具照樣重新排版,悄悄改動了文件。
格式化會改變文件的意思嗎?
不會,而這正是上面那條規則的用意。元素之間的空白屬於排版,會被取代;混合內容裡的空白屬於資料,會被保留。屬性順序與引號樣式完全照原樣保留,註解與 CDATA 原封不動複製,若文件原本沒有 XML 宣告也不會替它加上。
「格式正確」到底檢查了什麼?
檢查每個元素都有相符的結束標籤、屬性值都有引號、註解與 CDATA 區塊都有結束,以及剛好只有一個根元素且外面沒有多餘的文字。它不會依 DTD 或結構描述做驗證,那需要結構描述本身,屬於另一種工具。
我的屬性為什麼被拒絕?
XML 要求每個屬性值都要加引號。HTML 讓你寫 disabled=true 或 width=100,瀏覽器也能處理,但 XML 不行,後續每一個解析器都會拒絕。如果這份文件來自 HTML,通常這就是第一個該修的地方。
「壓縮」和「格式化」差在哪裡?
它們是對同一份文件的相反操作。格式化在元素之間插入換行與縮排讓它可讀,壓縮則把這些全部移除,讓文件在傳輸時盡可能小。兩者都不會動到混合內容,所以先做一個再做另一個,會回到原本的樣子。
我的 XML 會被上傳嗎?
不會。解析與格式化都在你的瀏覽器中完成,因此設定檔、SOAP 訊息與匯出的資料——這些經常包含內部主機名稱、識別碼與個人資料——不會離開你的電腦。

最後更新:

回報問題

請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。

相關工具