簡介
人們都很喜歡 XML 以及它所提供的靈活性和互操作性,但是,通過使用一些技巧,可以使與 XML 的互操作和與 XML 一起使用的工具更加簡單。在處理 XML 時養成一些良好的習慣可以保證最高效地利用您的 XML 文檔和應用程序。
使用 10 種良好的習慣
以下是 10 種最良好的 XML 習慣:
- 定義 XML 和編碼
- 使用 DTD 或 XSD
- 記得進行驗證
- 驗證并不總是能夠解決問題
- XML 結構和屬性
- 使用 XPath 查找信息
- 并不總是需要使用解析器提取信息
- 何時使用 SAX 而非 DOM 解析
- 何時使用 DOM 而非 SAX 解析
- 使用良好的 XML 編輯器
定義 XML 和編碼
 |
經常使用的縮寫詞
- DOM:文檔對象模型(Document Object Model)
- DTD:文檔類型定義(Document Type Definition)
- HTML:超文本標記語言(Hypertext Markup Language)
- IDE:集成開發環境(Integrated Development Environment)
- SAX:XML 簡單 API(Simple API for XML)
- XSD:XML 模式定義(XML Schema Definition)
- XML:可擴展標記語言(Extensible Markup Language)
- XSLT:可擴展樣式表語言轉換(Extensible Stylesheet Language Transformations)
|
|
在快速創建 XML 文檔時,一般都會傾向于創建基本的結構并避開一些普通 XML 文檔需求,包括指定 XML 文檔聲明和 XML 文檔包含的數據的編碼類型。
考慮清單 1 所示的 XML 文檔。
清單 1. 未包含 XML 聲明和數據編碼類型的 XML 文檔
<phrases>
<phrase lang="en">Hello</phrase>
<phrase lang="it">Buongiorno</phrase>
<phrase lang="fr">Salut!</phrase>
</phrases>
|
對于普通人來說,可以查看該文檔并將其識別為 XML,但是對于計算機來說,則很難作出這樣的判斷。在文件頂部添加 XML 聲明,可以使它更加明確、更容易識別。一行簡單的代碼就可以說明文檔是 XML,并且指出版本號和 XML 數據使用的字符編碼類型。例如:
<?xml version="1.0" encoding="us-ascii"?>
|
編碼說明中的內容也應該確保正確性。XML 解析器使用編碼確保 XML 文檔的單個字符被正確載入。例如,繼續 清單 1 中基于短語的示例,如果向文檔添加一個俄語條目,則會出現問題,因為目前指定的編碼不支持擴展的字符集(使用俄語短語表示 hello 時要求使用擴展字符集)。
指定錯誤的編碼意味著解析器不能正確處理文檔;例如,如果將一個多字節擴展字符讀取為一個單字節組成的序列,那么會導致數據的損壞和不良輸出。
使用 DTD 或 XSD
添加了 XML 聲明后,應該確保使用 DTD 或 XSD 定義有效的 XML 文件的結構。這兩種方法都允許 XML 解析器檢查并確定 XML 文件的內容與建模數據對應的結構相匹配。
例如,給出一個針對聯系(contact)數據庫的簡單 XML 結構,您希望定義一種結構來指定聯系人的姓名、地址和電話號碼。使用 DTD 方法可以使您映射這種結構并確保結構中的每一個聯系人與布局相匹配。
例如,清單 2 中顯示了針對聯系數據庫的 DTD。
清單 2. 用于聯系數據庫的 DTD
<!ELEMENT phone (#PCDATA)>
<!ATTLIST phone type (home | work | mobile) #REQUIRED>
<!ELEMENT contact (#PCDATA | name | phone | address)*>
<!ELEMENT contacts (#PCDATA | contact)*>
<!ELEMENT country (#PCDATA)>
<!ELEMENT road (#PCDATA)>
<!ELEMENT address (#PCDATA | road | city | state | postcode | country)*>
<!ATTLIST address type (home | work) #REQUIRED>
<!ELEMENT state (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT postcode (#PCDATA)>
<!ELEMENT city (#PCDATA)>
|
DTD 定義了描述聯系人所需要的元素、屬性(以及這些屬性所支持的值)。例如,在 清單 2 可以看到,phone 元素有一個 type 屬性,而 address 和其中的 component 元素也有屬性。
使用 DTD 可以幫助確保屬性的有效性,并且,和驗證過程一起使用時可以識別任何問題。當和支持 XML 的編輯器一起使用時,DTD 可以幫助編輯和自動完成內容。
XSDs,即模式,可以執行很多與 DTD 相同的功能,但有其獨特的用途。例如,一些 XML 編輯器需要使用 DTD 自動完成內容,然而模式在設計文檔實際層次結構方面更加靈活。您可以根據具體環境選擇工具。
記得進行驗證
查看清單 3,能否找出其中的問題?
清單 3. 驗證示例
<contacts>
<contact>
<name>Martin</name>
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
<address type="home">
<road>Home road</road>
<city>Home city</city>
<state>Home state</state>
<zipcode>12434</zipcode>
<country>USA</country>
</address>
</contact>
<contact>
<name>Sharon</name>
<phone type="work">234 567 8901</phone>
<phone>234 567 8901</phone>
<address type="home">
<road>Other home road</road>
<city>Other city</city>
<state>Other state</state>
<zipcode>39487</zipcode>
<country>USA</country>
</address>
<address type="work>
<road>Work building, work road</road>
<city>Work city</city>
<state>Work state</state>
<zipcode>12347</zipcode>
<country>USA</country>
</address>
</contact>
</contacts>
|
手動查找問題非常麻煩。但是可以通過 xmllint(一款可以檢驗 XML 文件的內容和結構的免費工具)運行文件,您可以查看運行該文件的輸出,如清單 4 所示。
清單 4. 通過 xmllint 運行清單 3 得到的輸出
$ xmllint contacts.xml
contacts.xml:27: parser error : Unescaped '<' not allowed in attributes values
<road>Work building, work road</road>
^
contacts.xml:27: parser error : attributes construct error
<road>Work building, work road</road>
^
contacts.xml:27: parser error : Couldn't find end of Start Tag address line 26
<road>Work building, work road</road>
^
contacts.xml:32: parser error : Opening and ending tag mismatch: contact line 15
and address
</address>
^
contacts.xml:33: parser error : Opening and ending tag mismatch: contacts line 1
and contact
</contact>
^
contacts.xml:34: parser error : Extra content at the end of the document
</contacts>
|
盡管與最初的問題(其中的一個屬性沒有結束)相比似乎復雜了很多,但卻為您查找問題提供了一個起點。
xmllint 還支持各種各樣的命令行選項,幫助選擇診斷方法和結果。其中最有用的一個選項是 --noout
,它阻止 xmllint 在解析文件時回傳內容。對于較短的文件來說沒有什么影響,但對于大型文件來說則是一個問題。
如果正在使用 DTD,那么使用 --postvalid
選項告訴 xmllint 針對 DTD 驗證內容,確保內容不僅是有效的 XML,而且還與 DTD 的結構相匹配。如果將 使用 DTD 或 XSD 為 contact 文件生成的 DTD 添加到文件,那么屬性定義錯誤將被糾正,但隨后將生成一個不同的錯誤,如清單 5 所示。
清單 5. xmllint 找到不同的錯誤
$ xmllint --noout --postvalid contacts.xml
contacts.xml:9: element address: validity error : Element zipcode is not declared
in address list of possible children
contacts.xml:21: element address: validity error : Element zipcode is not declared
in address list of possible children
contacts.xml:28: element address: validity error : Element zipcode is not declared
in address list of possible children
Document contacts.xml does not validate
|
這樣使用 xmllint 可以方便快捷地確定文檔的結構是否有效。xmllint 是 libxml2 工具箱的一部分,該工具箱已綁定到 Linux、UNIX® 和 Mac OS X,但 Windows® 需要獨立下載。有關 xmllint 和 libxml2 的更多信息,請參見 參考資料。
驗證并不總是能夠解決問題
使用 xmllint 和類似工具驗證 XML 文件(特別是如果使用了 DTD),是驗證 XML 文件內容的很好方法。然而,這種方法也有其局限性。例如,如何處理 XML 文件的內容?
使用 DTD 或 XSD,您可以為屬性指定明確的內容。您只是創建了帶有一個字符串或 ID 的屬性(可以是受限制的可用選項列表的一部分),但是不能使用這種方式控制或限制元素的內容。
例如,在聯系人示例中,telephone numbers 元素包含數字和空格。但是沒辦法阻止用戶向該元素添加字母字符。這樣做在使用 xmllint 進行驗證時不會檢查出錯誤,并且編輯器和其他支持 XML 的解決方案也無法解決或識別這個問題。應用程序出現的故障可能和您預料的一樣,因為它識別出一個非標準數據類型。
簡而言之,XML 驗證只能保證結構正確,而無法保證數據的有效性。
解決此問題的最簡單方法是編寫一個解析器,它可以讀取 XML 文件并實際驗證數據內容。但是不要過度地驗證內容,只需確保數據符合應用程序的要求。
XML 結構和屬性
對于究竟是使用屬性還是元素來描述希望在 XML 文件中呈現的信息,人們存在著不同的看法。
一般的做法是,使用元素(即標記之間的數據)定義文件包含的信息,而屬性則用于提供所描述數據的擴展限制。
元素和屬性都各有弊端。例如,屬性不能夠在標記中重復,這是元素優于屬性的典型例子。元素支持重復信息的能力使其非常實用。相反,使用元素限制數據有時處理起來會比較復雜。
聯系人示例中的電話號碼很好地解釋了屬性的優點。在這個示例中,如清單 6 所示,使用屬性限制電話號碼的類型(例如辦公、住宅或移動電話)。
清單 6. 限制電話號碼的類型
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
|
使用這種結構,能夠輕松地把號碼作為一個整體(忽略屬性),或者挑選特定的電話號碼類型(使用屬性)。
將此結構與清單 7 中只使用元素設計的結構進行比較。
清單 7. 只使用元素限制電話號碼
<phone>
<type>home</type>
<number>123 456 7890</number>
</phone>
<phone>
<type>mobile</type>
<number>123 456 7890</number>
</phone>
<phone>
<type>work</type>
<number>123 456 7890</number>
</phone>
|
現在還很難判斷孰優孰劣。盡管從理論上說任何 XML 解析器或適當的 XPath 定義都可以把您需要的信息抽取出來。但這樣做獲益不大,并且使得 XML 文檔很難閱讀。
使用 XPath 查找信息
在處理 XML 數據時,查找需要的信息非常復雜。您可以編寫一個解析器來挑選需要的信息,但在某些情況下,您只需要快速地找到文件中的一小段信息。
例如,如果需要從聯系人 XML 文件中提出所有國家的列表,以便查看聯系人在全球的分布范圍,那么可以使用 XPath 來挑選信息。
通過將 XML 文件的結構作為查詢的一部分,XPath 使您能夠從 XML 文件中抽取數據。例如,通過提供特定元素在 XML 文件中的路徑,您可以提取該元素的數據:
$ xpath contacts.xml '//contact/address/country'
|
您可以按照下面這樣分析內容:
- 最開頭的雙斜杠(//)表示在文檔的任意位置查找指定的元素(contact)。
- 下一個斜杠和元素名指定了要查找的下一個元素(address)— 就是說,在 contact 元素內查找 address 元素。
- 最后的斜杠重復此過程,這一次查找的是 country 元素。
注意,在這個示例中,您限定了從中選擇信息的地址的類型,因此將選擇所有地址。您可以在清單 8 中查看 XPath 查詢的結果。
清單 8. XPath 查詢的結果
$ xpath contacts.xml '//contact/address/country'
Found 3 nodes:
-- NODE --
<country>USA</country>-- NODE --
<country>USA</country>-- NODE --
<country>USA</country>
|
如果需要挑選更具體的數據,可以指定要匹配的元素或屬性的內容。例如,如果只選擇手機號碼,您需要指定屬性類型和值。為此,使用(@)符號,它表示您要求搜索一個屬性,然后指定需要匹配的值(參加清單 9 )。
清單 9. 只選擇手機號碼
$ xpath contacts.xml '//contact/phone[@type="mobile"]'
Found 1 nodes:
-- NODE --
<phone type="mobile">123 456 7890</phone>
|
清單 8 和 9 都使用了一個命令行工具。很多 XML 工具箱都提供了原生方法來處理 XPath 元素,并且您可以使用 XPath 規范提取數據在應用程序中直接使用,而不需要使用解析器來獲取信息。
并不總是需要使用解析器提取信息
盡管有些意想不到,但您并一定需要使用一種功能完善的 XML 解析器,使用 SAX、DOM 或其他技術(如 XPath 或 XQuery)從 XML 文件中提取需要的信息。
XML 文件使用結構化的格式包含數據,但是有時您需要信息使用自身的結構化格式。要快速查找一個信息片段時,通常可以使用更簡單的解決方法。
通常,您僅需使用 grep、Perl 或其他類似工具提取所需的數據,而不需要以 XML 文件的形式實際解釋文檔的結構或內容。
例如,您可以使用 grep 選擇電話號碼(參見清單 10)。
清單 10. 使用 grep 選擇電話號碼
$ grep '<phone' contacts.xml
<phone type="home">123 456 7890</phone>
<phone type="mobile">123 456 7890</phone>
<phone type="work">123 456 7890</phone>
<phone type="work">234 567 8901</phone>
<phone>234 567 8901</phone>
|
您使用 grep 選擇了需要的信息,并且不需要考慮信息是 XML 格式或者信息的結構。
如果需要查找簡短的信息片段,簡化的處理技術可以查找到所需的信息,并且避免了使用傳統解析方法的開銷。
何時使用 SAX 而非 DOM 解析
當為文檔構建一個解析器以獲得所需信息時,常常很難決定何時使用基于 SAX 的處理程序,何時使用基于 DOM 的處理程序。
關于這個問題的最簡單解決方法是同時考慮文檔的復雜性和所查找信息的用途。如果要轉換文檔,或者文檔非常大,那么 SAX 是最佳選擇。
SAX 逐個解析文檔元素,在識別元素時調用方法或函數。如果將一個 XML 文檔轉換為另一種格式,例如將 XML 轉換成 HTML,那么 SAX 是最有效的方式。您不必將整個文檔加入到內存中,只需響應被識別出的元素和結構。
SAX 的缺點是,如果需要保存或記錄結構,或者理解整個文檔并從其中挑選單個元素(例如,從所有記錄中選擇單個聯系人),則必須構建復雜的處理程序,以加載數據并將數據記錄到結構中,然后將元素標識到輸出目標中。
何時使用 DOM 而非 SAX 解析
DOM 可以將整個文檔及其結構載入到內存,并允許您在應用程序內部引用和使用 XML 文檔的結構。如在聯系人示例中,您可以將整個聯系人數據庫讀入內存,然后通過遍歷聯系人選擇所有的電話號碼,接著在每個聯系人內部遍歷每個電話號碼。
由于 DOM 保留了結構,更重要的是可以理解和處理結構,您可以輕松地對結構進行整體或單獨的處理。仍然以聯系人示例為例,使用 SAX 插入新的聯系人將非常復雜。但是如果使用 DOM,您只需將一個表示新聯系人的新 XML 元素插入到現有的 XML 文檔。
DOM 的缺陷是使用流方式處理文件 — 例如,轉換為 HTML — 過于復雜,因為必須在結構內逐個遍歷每個元素來處理文檔。
此外,由于 DOM 在解析其間將整個 XML 文檔載入到內存中,DOM 解析器會變得非常慢并且需要更多的內存。但 DOM 處理這樣做也有一些好處;例如,在一次解析過程中,可以對使用 DOM 解析的 XML 文檔進行多次處理。而使用 SAX,則需要多次重復解析過程才能獲得相同的效果。
訪問 參考資料,查找更多關于使用 DOM 和 SAX 的信息。
使用良好的 XML 編輯器
如果經常需要編寫和使用 XML,那么必須擁有一個良好的 XML 編輯器。XML 編輯器不同于標準的文本編輯器,前者可以理解 XML 的結構和布局。XML 編輯器提供的豐富特性使處理 XML 更加簡單,這些特性包括:
- 完成 — 為一個快完成的元素輸入字符,編輯器可以自動幫您輸入剩下的內容。
- 內容完成 — 如果對 XML 文件使用 DTD,那么編輯器可以為您填充并格式化部分內容。例如,在 contacts DTD 中,phone 元素的 type 屬性是一個必需元素。使用智能 XML 編輯器,在創建 phone 標記時,該屬性(值為空)將自動引入到文本中。
- 內聯格式化 — 編輯器可以使您的 XML 更加易于閱讀和理解。這可以在編輯時立即實現,也可以通過單獨的格式命令實現。最終得到可以理解并可以更快速地標識的 XML。
- 內置驗證 — 在輸入內容時,編輯器可以驗證 XML 文檔的錯誤,在編輯器中立即突出顯示各種問題,這樣您就知道該如何解決這些問題。
- 內置翻譯和轉換 — 一些 XML 編輯器包括 XPath、XQuery 界面,某些情況下還包括 XSLT 和其他轉換的界面,因此可以在編輯環境中查看轉換結果。
- 學習和操作 — 有時您在 DTD 之前創建 XML 結構。在這種情況下,編輯器可以讀取 XML 文件,學習它的結構并創建一個 DTD 進行驗證,這樣可以為您節省大量的時間和精力。
好的 XML 編輯器包括 Eclipse 和 oXygenXML,但是還有很多其他選擇。
結束語
養成良好的 XML 處理習慣,一切將大有不同,包括利用 XML 提供的功能、打破 XML 標準進行驗證的基礎知識以及正確處理解析。本文可以幫助您學習這 10 個好習慣,從而提高處理 XML 文檔和數據的效率。