關於本教程
在本教程中,我們將討論如何使用一個 XML 解析器來:
處理一個 XML 文檔
建立一個 XML 文檔
操作一個 XML 文檔
我們也將討論一些有用而不為眾人所知的 XML 解析器特性。 最重要的,我們所討論的每個工具都可從 IBM 的 alphaWorks 網站 (www.alphaworks.ibm.com) 和其它網站免費獲得。
未討論的:
有些重要的編程概念並未在此介紹:
1 使用可視工具來構建 XML 應用
2 將一個 XML 文檔從一種形式轉換到另一種
3 為終端使用者或其他進程建立介面,及對後端儲存資料的介面
當您構建一個 XML 應用時,所有這些概念都是重要的。我們正在編製新的教程來討論它們,因此請常光顧我們的網址!
XML 應用架構
一個 XML 應用通常是基於一個 XML 解析器而構建的。它為其使用者提供了一個介面,以及對後端儲存資料的一個介面。
本教程關注於編寫使用 XML 解析器來操作 XML 文檔的 Java 代碼。如下邊圖片所示,本教程關注於中間那塊。
第二章 解析器基礎
基礎
一個 XML 解析器是一段可以讀入一個文檔並分析其結構的代碼。在本章節,我們將討論如何使用一個 XML 解析器來讀入一個 XML 文檔。我們也將討論不同類型的解析器以及您在何時使用它們。
本教程後面的章節將討論您從解析器可以獲得什麼以及如何使用這些結果。
如何使用一個解析器
我們將在稍後的章節詳細討論它,但通常而言,您如下使用它:
1 建立一個解析器對象
2 將您的 XML 文檔傳遞給解析器
3 處理結果
構建一個 XML 應用顯然遠遠超出這些,但通常一個 XML 的應用將包含這些流程。
解析器種類
有不同的方法來劃分解析器種類:
驗證或非驗證解析器
支援 Document Object Model (DOM) 的解析器
支援 Simple API for XML (SAX) 的解析器
用特定語言編寫的解析器 (Java, C++, Perl 等)
驗證或非驗證解析器
如我們在第一個教程中所提及的,XML 文檔如果使用一個 DTD 並符合 DTD 中的規則將被稱為有效文檔(valid document)。符合基本標記規則的 XML 文檔被稱為格式正確文檔(well-formed document)。
XML 規範要求所有的解析器當其發現一個文檔不是格式正確時要報錯。驗證(Validation)則是另一個問題了。驗證解析器(Validating parser)在解析 XML 文檔同時進行驗證。非驗證解析器(Non-validating parser) 忽略所有的驗證錯誤。換而言之,如果一個 XML 文檔是格式正確的時,一個非驗證解析器並不關注文檔是否符合其對應 DTD 所指定的規則(如果有的話)。
為何使用非驗證解析器?
速度和效率。要一個 XML 解析器處理 DTD 並確保每個 XML 的元素符合在 DTD 中的規則需要相當大的開銷。如果您確定一個 XML 文檔是有效(可能來自一個資料來源),那就沒有必要在次驗證它了。
同樣,有時您所需要的只是從一個文檔中找出 XML 的標記。一旦您有了這些標記,您可以將資料從中提取出然後加以處理。如果這就是您所需要的,一個非驗證解析器就是正確的選擇。
Document Object Model (DOM)
文件物件模型(Document Object Model)是 World Wide Web Consortium(W3C) 的正式推薦。它定義了一個介面使得程式可以存取和更新 XML 文檔的風格、結構和內容。支援 DOM 的 XML 解析器實現該介面。
您需要十分瞭解文檔的結構
您需要操作文檔中的某些部分(例如,您可能想對某些元素排序)
您需要不止一次使用文檔中的資訊
當您只需要從一個 XML 文檔中提取若干元素時,可使用 SAX 解析器。SAX 解析器在您沒有大多數記憶體時、或者如果您只需要使用文檔中的資訊一次(而不是解析文檔一次,而後要反覆使用它)。
不同語言的 XML 解析器
在 Web 上使用的大多數語言都有其對應的 XML 解析器和庫,包括 Java、C++、Perl 和 Python。下一頁介紹了 IBM 或其它公司提供的解析器的連結。
本教程中絕大多數的樣本是使用 IBM 的 XML4J 解析器。我們所討論的所有代碼使用標準的介面。在本教程的最後章節,我們將向您展現編寫可使用不同解析器的代碼是如何簡單。
Java
IBM 的解析器,XML4J,可從 www.alphaWorks.ibm.com/tech/xml4j 獲得。
James Clark 的解析器,XP,可從 www.jclark.com/xml/xp 獲得。
Sun 的 XML 解析器可從 developer.java.sun.com/developer/products/xml/ (您必需成為 Java Developer Connection 的會員)下載。
DataChannel 的 XJParser 可從 xdev.datachannel.com/downloads/xjparser/ 獲得。
C++
IBM 的 XML4C 解析器可從 www.alphaWorks.ibm.com/tech/xml4c 獲得。
James Clark 的 C++ 解析器,expat,可從 www.jclark.com/xml/expat.html 獲得。
Perl
有多種 Perl 語言的 XML 解析器。要獲得更多資訊,參見 www.perlxml.com/faq/perl-xml-faq.html。
Python
要獲得更多 Python 語言的 XML 解析器,參見 www.python.org/topics/xml/。
總結
任何 XML 應用的核心是一個 XML 解析器。要處理一個 XML 文檔,您的應用將建立一個 parser 對象,將一個 XML document 傳遞給它,然後處理從 parser 對象返回的結果。
我們討論了不同類型的 XML 解析器,以及您為何選取其一。我們用不同方式來對解析器分類:
驗證或非驗證解析器
支援 Document Object Model (DOM) 的解析器
支援 Simple API for XML (SAX) 的解析器
用特定語言編寫的解析器 (Java, C++, Perl 等)
在我們的下一章節,我們將探討 DOM 解析器和如何使用它們。
在 main 方法中,我們處理命令列,建立一個 domOne 對象,然後將檔案名稱傳遞給 domOne 對象。domOne 對象建立一個 parser 對象,解析文檔,然後通過 printDOMTree 方法處理 DOM 樹 (即 Document 對象)。
我們將詳細研究每個步驟。
public class domOne
{
public void parseAndPrint(String uri)
...
public void printDOMTree(Node node)
...
public static void main(String argv[])
...
try
{
DOMParser parser = new DOMParser();
parser.parse(uri);
doc = parser.getDocument();
...
if (doc != null)
printDOMTree(doc);
}
處理 DOM 樹
現在解析已經完成,我們將遍曆 DOM 樹。注意這段代碼是遞迴的。對每個節點,我們處理其本身,然後我們對每個節點的子女遞迴地調用 printDOMTree 方法。遞迴調用如左所示。
要記住當有些 XML 文檔非常大時,它們反而不會有太多層標記。以一個上海市的電話簿為例,可能有幾百萬條記錄,但其標記可能不會超過幾層。考慮到這個原因,遞迴演算法的棧溢出不是一個問題。
public void printDOMTree(Node node)
{
int nodeType = Node.getNodeType();
switch (nodeType)
{
case DOCUMENT_NODE:
printDOMTree(((Document)node).
GetDocumentElement());
...
case ELEMENT_NODE:
...
NodeList children =
node.getChildNodes();
if (children != null)
{
for (int i = 0;
i < children.getLength();
i++)
printDOMTree(children.item(i));
}
當您構建一個 XML 文檔時不需要考慮可讀性,就可省略分行符和空格符。這可使得您的文檔更小,處理您的文檔時也不需要構建那些無用的節點。
<sonnet type="Shakespearean">
<author>
<last-name>Shakespeare</last-name>
<first-name>William</first-name>
<nationality>British</nationality>
<year-of-birth>1564</year-of-birth>
<year-of-death>1616</year-of-death>
</author>
<title>Sonnet 130</title>
<lines>
<line>My mistress' eyes are nothing like the sun,</line>
一個 Text 節點對應於 "Shakespeare" 字元
如果您看到標記間所有的空格符,您可發現為何我們有那麼多超出您想像的節點。
瞭解您的 Node
我們最後對處理在 DOM 樹的 Node 要指出的是,我們在處理其之前要檢查每個 Node 的類型。一些方法,例如 getAttributes,對一些特定的節點類型返回 null 值。如果您不檢查節點類型,您將得到不正確的結果(最佳情況)和異常(最差情況)。
在此所介紹的 switch 語句常出現在使用 DOM 解析器的代碼。
switch (nodeType)
{
case Node.DOCUMENT_NODE:
...
case Node.ELEMENT_NODE:
...
case Node.TEXT_NODE:
...
}
總結
不管您信不信,這就是我們使用 DOM 對象所要瞭解的所有內容。我們的 domOne 程式碼完成了下列工作:
建立一個 Parser 對象
將一個 XML 文檔傳遞給 Parser 來解析
獲得來自於 Parser 的 Document 對象然後加以檢查。
在本教程最後一章,我們將討論如何不需要 XML 原檔案來構建一棵 DOM 樹,並展示如何對一個 XML 文檔中的元素排序。而那些都是基於我們這裡所討論的概念之上。
在我們繼續那些更進階的應用前,我們將詳細探討 SAX API。我們同樣將使用類似的樣本,展現 SAX 和 DOM 的不同處。