XML 檔案格式
Extensible Markup Language,通用的結構化文字格式。
- MIME 類型
- application/xml
- 副檔名
- .xml
- 分類
- 文字與標記語言
Magic number(檔案簽章)
以 '<?xml' 宣告開頭的純文字。
關於 XML 格式
XML 是一種由「格式良好」規則定義的文字格式,而不是由任何位元組配置定義:一個根元素、每個元素都要關、屬性值要加引號,還有一小組必須逸出的字元。至於某一份 XML 文件的其他一切——元素叫什麼名字、什麼樣的巢狀合法、這些東西是什麼意思——都定義在標準之外,由 schema 或某個剛好拿 XML 當語法的規格決定。這就是為什麼一大堆彼此無關的東西同時都是 XML:Office 文件、EPUB、RSS 與 Atom feed、SVG、Android 版面配置、XMP 中繼資料、sitemap 和 property list 全都是,而它們沒有一個讀得懂另一個。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 3 | UTF-8 byte-order mark | EF BB BF,選用。它是標準唯一允許出現在宣告前面的東西;第一個角括號之前只要有別的位元組,這份文件就不是格式良好,這條規則值得背起來,因為它把一整類 bug 變成兩秒鐘的 hex 檢查。 |
| 0 | 2 | UTF-16 byte-order mark | 大端序是 FE FF,小端序是 FF FE。和 UTF-8 標記不同,這一個不是選用的:標準規定以 UTF-16 編碼的實體必須以標記開頭,而 UTF-8 的實體只是「可以」。UTF-16 的 XML 很罕見,而且會弄壞任何假設「一個字元一個位元組」的工具。 |
| 0 | 5 | XML declaration opener | 3C 3F 78 6D 6C,宣告開頭的五個字元。它位在位移 0,或是在 UTF-8 標記後面的位移 3,除此之外哪裡都不會。對 UTF-8 內容而言它是選用的,這正是為什麼沒有偵測器能硬性要求它,也是為什麼那麼多 XML 只能靠根元素來辨識。 |
| 0 | 4 | UTF-16 declaration without a mark | 大端序是 00 3C 00 3F,小端序是 3C 00 3F 00。標準的自動偵測附錄列出這些樣式,理由很循環、也很值得體會:解析器得先猜對編碼,才讀得到那份「說明編碼是什麼」的宣告。 |
| 5 | variable | Declaration pseudo-attributes | version 排第一而且是必要的,接著是選用的 encoding,再來是選用的 standalone,順序就是這樣、不能換。encoding 的值是一項「關於這些位元組」的宣稱,不是關於它們的事實;相信它的解析器會在第一個和它矛盾的位元組上中止,而那個位置離真正出錯的地方通常很遠。 |
| 0 | 5 | DOCTYPE opener | 3C 21 44 4F 43,而它位在位移 0 的前提是:沒有宣告、沒有標記、開頭也沒有空白。否則它就落在 prolog 剛好結束的地方——這是老實的答案,也說明了為什麼找它該用解析器而不是位元組位移表。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| HTML | 兩者都是角括號,也都沒有 magic number,但瀏覽器採用的嗅探規則把它們分得很乾淨。XML 是在可有可無的空白之後精確比對位元組 3C 3F 78 6D 6C;HTML 則是比對一張標籤名稱樣式表——DOCTYPE HTML、HTML、HEAD、SCRIPT、IFRAME、TITLE、BODY 等十幾種——而且每一個後面都必須接空白或右角括號才算數。更深層的差別在文件出錯的時候:XML 解析器會停下來,HTML 解析器會自己復原。 |
| SVG | SVG 就是 XML。位移 0 也好、其他任何地方也好,沒有任何位元組把它們分開;差別只在根元素叫 svg、而且帶著 SVG 命名空間。一個工具說某檔案是 svg、另一個工具說同一個檔案是 xml,兩邊都是對的,而任何非得二選一的偵測器,做的是編輯決定而不是技術判斷。 |
| Android binary XML | APK 裡的 AndroidManifest.xml 根本不是文字。它是一種以 chunk 組成的二進位編碼,前四個位元組是 03 00 08 00——小端序的 chunk 型別 3 加上標頭大小 8——後面接文件長度。用文字編輯器打開只會看到二進位雜訊,因為文件裡每個字串都被搬到檔案別處的字串池,元素則用索引去指它們。 |
| Word 文件(docx) | 大家想在 Office 檔案裡找的那些 XML 確實存在,但磁碟上這個檔案開頭是 50 4B 03 04,因為它是 ZIP。.docx、.xlsx、.pptx 前面沒有任何東西是 XML;那些元件是壓縮檔裡的項目,而其中一個叫 [Content_Types].xml 的項目,才是讓這個套件成為 Office 文件、而不是一個剛好裝了標記的普通 ZIP 的關鍵。 |
常見的壞法
- prolog 裡混進垃圾。XML 只允許宣告前面有一個位元組順序標記,除此之外什麼都不行,所以一行空白、指令碼印出來的一句警告,或是內容前面的一頁 HTML 錯誤頁,都會產生每個 XML 開發者都見過的那個錯誤:content is not allowed in prolog。它永遠只代表一件事——第一個角括號前面有東西——而 hex 檢視器幾秒鐘就找得出來。
- 編碼宣告在說謊。宣告寫 UTF-8、位元組其實是 Windows-1252,或者反過來。一切都解析得好好的,直到第一個超過 0x7F 的字元,此時解析器報無效的位元組序列,然後指著一行畫面上看起來再普通不過的文字——因為那個惹禍的字元在寫出它的編輯器裡顯示得好端端的。
- 被截斷。XML 沒有長度欄位、也沒有尾標,所以文件被切掉這件事,一直要到解析器把輸入讀完、卻還有元素沒關才發現得了。訊息是資料提前結束,而少了多少東西,光靠這個檔案本身完全推不出來。
- 匯出資料裡混進控制字元。XML 1.0 在 C0 範圍裡只允許 tab、換行與歸位,其餘一律禁止,就算寫成數值字元參照也一樣。所以帶著 0x00 或 0x0B 的資料庫匯出檔,會產生一個看起來格式良好、肉眼檢查也過關、卻沒有任何一個符合標準的解析器願意接受的檔案。
- 沒有逸出的 & 與角括號。用字串串接而不是序列化器組出來的標記,只要某個值裡出現 & 或 <,當場就垮。解析器會回報「實體參照後面應該要有分號」,位置指在資料裡而不是標記裡,這也是為什麼要修的地方幾乎從來都不在錯誤指的位置。
這個格式現在還在用嗎
XML 丟掉了它在 2005 年佔住的多數地盤,守住了真正重要的那些。網頁 API 與設定檔被 JSON 取代,接著是 YAML 與 TOML,而 SOAP 在還在跑的地方幾乎都是舊系統。它守住的是文件:任何有混合內容、文字與標記交錯而且順序有意義的東西,用 XML 遠比用資料格式合適,這就是為什麼 Office、ODF、EPUB、DocBook 和 TTML 字幕沒有搬家,也不打算搬。它也守住了「驗證才是重點」的場合,因為 XML Schema、XPath 與 XSLT 在 JSON 那一側沒有真正的對應物——所以任何在處理前必須先對照契約檢查過的格式,不論是政府申報、發票、feed 還是 Android 版面配置,通常還是以 XML 送達。而拿它到 hex 編輯器裡看很不尋常的一點是:它完全沒有標頭,檔案的第一個位元組就已經是內容了。
在這個站上打開 XML 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 XML 搞混的格式,可以比對它們的開頭位元組。