JAVA XML教程

 
第1章              入門

關於本教程  
在本教程中,我們將討論如何使用一個 XML 解析器來: 
處理一個 XML 文檔 
創建一個 XML 文檔 
操作一個 XML 文檔 
我們也將討論一些有用而不爲衆人所知的 XML 解析器特性。 最重要的,我們所討論的每個工具都可從 IBM 的 alphaWorks 站點 (www.alphaworks.ibm.com) 和其它網站免費獲得。 

未討論的:

有些重要的編程概念並未在此介紹:
1 使用可視工具來構建 XML 應用 
2 將一個 XML 文檔從一種形式轉換到另一種 
3 爲最終用戶或其他進程創建接口,及對後端存儲數據的接口
當您構建一個 XML 應用時,所有這些概念都是重要的。我們正在編制新的教程來討論它們,因此請常光顧我們的網址!

XML 應用架構 

一個 XML 應用通常是基於一個 XML 解析器而構建的。它爲其用戶提供了一個接口,以及對後端存儲數據的一個接口。 

本教程關注於編寫使用 XML 解析器來操作 XML 文檔的 Java 代碼。如下邊圖片所示,本教程關注於中間那塊。 

第2章                                        解析器基礎

基礎

一個 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 解析器實現該接口。

該規範的第一版,DOM Level 1,可從 http://www.w3.org/TR/REC-DOM-Level-1 獲得, 如果您願意閱讀規範的話。

DOM 解析器可提供什麼

當您用一個 DOM 解析器來解析一個 XML 文檔時,您將獲得一個包含文檔中所有元素的樹結構。DOM 提供了不同的功能來檢查文檔的內容和結構。

關於標準 

現在我們即將討論開發 XML 應用了,我們也要關注 XML 的標準。正式而言,XML 是 MIT(麻省理工) 的商標和 World Wide Web Consortium (W3C)組織的產品。 

XML 規範,W3C 的正式推薦,可從 www.w3.org/TR/REC-xml 下載。W3C 站點包含了 XML、DOM 以及一大堆 XML 相關標準的規範。

Simple API for XML (SAX) 

SAX API 是另一種處理 XML 文檔內容的方法。一個既成事實的標準,它由 David Megginson 和 XML-Dev 郵件列表其它成員所開發。

要查看完整的 SAX 標準,參見 www.megginson.com/SAX/。要參加 XML-Dev 郵件列表,發送郵件到 [email protected] 其中包含: subscribe xml-dev。

SAX 解析器可提供什麼 

當您使用 SAX 解析器來解析 XML 文檔時,解析器在文檔的不同處將產生事件。由您來決定對每個事件如何處理。

SAX 解析器會在以下情況下產生事件:在文檔開始和結束時,在一個元素開始和結束時,或者它在一個元素中找到字符時,以及其它若干點。您可編寫 Java 代碼來處理每個事件,以及如何處理從解析器獲得的信息。

何時使用 SAX?何時使用 DOM? 

我們將在稍後的章節詳細討論這個問題,但通常而言,您在下列時候應該使用一個 DOM 解析器:

您需要十分了解文檔的結構 
您需要操作文檔中的某些部分(例如,您可能想對某些元素排序) 
您需要不止一次使用文檔中的信息
當您只需要從一個 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 解析器和如何使用它們。

第三章 DOM (Document Object Model)
Dom, dom, dom, dom, dom, 
Doobie, doobie, 
Dom, dom, dom, dom, dom...

DOM 是一個操作文檔結構的通用接口。它設計的一個目標是爲一個 DOM 兼容解析器所編寫的 Java 代碼應該可以使用其它任意 DOM 兼容的解析器而不需要修改代碼。(我們稍後將展示這個。)

正如我們前面所提的,一個 DOM 解析器將以樹形式返回您整個文檔的結構。

示例代碼

在我們繼續以前,請您下載我們的示例 XML 應用程序。解開此文件 xmljava.zip,就可以了!(blueski:***或者查看本教程附錄)

DOM 接口

DOM 定義了多個 Java 接口。下列是常用的:

Node: DOM 基本的數據類型。 
Element: 您將最主要處理的對象是 Element。 
Attr: 代表一個元素的屬性。 
Text: 一個 Element 或 Attr 的實際內容。 
Document: 代表整個 XML 文檔。一個 Document 對象通常也被稱爲一棵 DOM 樹。

常用的 DOM 方法

當您使用 DOM 時,下列是您將常會使用的方法:

Document.getDocumentElement() 
返回文檔的根(root)元素。 
Node.getFirstChild() and Node.getLastChild() 
返回給定 Node 的第一個子女。 
Node.getNextSibling() and Node.getPreviousSibling() 
它將刪除 DOM 樹中一切內容,格式化您的硬盤,然後給您地址簿中每個人發送一個謾罵的郵件。(不是真的啦。這些方法返回下一個或前一個給定 Node 的同胞。) 
Node.getAttribute(attrName) 
對給定的 Node,返回給定名稱的屬性。例如,如果您要獲得名爲 id 屬性 的對象,可調用 getAttribute("id")。 

我們的第一個 DOM 應用! 

介紹了很多概念,讓我們繼續吧。我們的第一個應用簡單地讀入一個 XML 文檔並將其內容輸出到標準輸出。

在一個命令行窗口,運行下面的命令:

java domOne sonnet.xml

這個命令將載入我們的應用然後讓它解析 sonnet.xml 文件。如果一切運行正常,您將看到 XML 文檔的內容被輸出到標準輸出。
<?xml version="1.0"?> 
<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 ... 

domOne 剖析 

domOne 的源碼是非常直了的。我們創建一個新的類 domOne;它有兩個方法,parseAndPrint 以及 printDOMTree。 

在 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[]) 
... 

處理命令行 

處理命令行的代碼在左面顯示。我們將檢查用戶是否在命令行上輸入參數。如果沒有,我們打印使用方法並推出;否則,我們假定命令行上第一個參數( Java 語言中的 argv[0] ) 是文檔名。我們忽略用戶可能輸入的其它參數。

我們使用命令行參數來簡化我們的示例。在大多數情況下,一個 XML 應用可能使用 servlet、Java Bean 和其它類型的組件一起使用;而用命令行參數並不是一個問題。

public static void main(String argv[]) 

if (argv.length == 0) 

System.out.println("Usage: ... "); 
... 
System.exit(1); 


domOne d1 = new domOne(); 
d1.parseAndPrint(argv[0]); 

創建一個 domOne 對象 

在我們的示例代碼中,我們創建一個單獨的類 domOne。要解析文件和打印結果,我們創建一個 domOne 類的實例,然後讓我們剛創建的 domOne 對象來解析和打印 XML 文檔。

我們爲何這樣處理?由於我們想要使用一個遞歸方法來遍歷 DOM 樹並打印出結果。我們無法用一個如 main 的靜態方法來處理,因此我們創建一個單獨的類來處理它。


public static void main(String argv[]) 

if (argv.length == 0) 

System.out.println("Usage: ... "); 
... 
System.exit(1); 


domOne d1 = new domOne(); 
d1.parseAndPrint(argv[0]); 

創建一個 Parser 對象 

現在我們已經讓 domOne 的實例來解析和處理我們的 XML 文檔,它的第一個處理是創建一個新的 Parser 對象。在此例中,我們將使用一個 DOMParser 對象,一個實現 DOM 接口的 Java 類。在 XML4J 包中還有其它 parser 對象,例如 SAXParser、ValidatingSAXParser 和 NonValidatingDOMParser。 

注意我們將這段代碼放在一個 try 模塊中。parser 在某些情況下將拋出異常(exception),包括一個無效的 URI、找不到一個 DTD 或者一個 XML 文檔不是有效的或格式錯誤。要很好地處理它,我們要捕獲異常(exception)。

try 

DOMParser parser = new DOMParser(); 
parser.parse(uri); 
doc = parser.getDocument(); 


解析 XML 文檔 

解析文檔只是簡單的一行代碼。當解析結束時,我們獲得解析器生成的 Document 對象。

如果 Document 對象不是 null (如果解析過程出錯它將是 null),我們將其傳遞給 printDOMTree 方法。

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));
}

很多 Node

如果您查看 sonnet.xml,有二十四個節點。您可能認爲這意味着二十四個節點。然而,這不正確。在 sonnet.xml 中一共有 69 個節點;一個文檔節點(document node), 23 個元素節點(element node)以及 45 個文本節點(text node)。我們運行 java domCounter sonnet.xml 就獲得了下邊所示的結果。

domCounter.java

這段代碼解析一個 XML 文檔,然後遍歷 DOM 樹來採集有關該文檔的數據。當數據採集後將其輸出到標準輸出。


統計 sonnet.xml 的數據:
====================================
Document Nodes: 1
Element Nodes: 23
Entity Reference Nodes: 0
CDATA Sections: 0
Text Nodes: 45
Processing Instructions: 0
----------
Total: 69 Nodes

節點列表示例

對於下邊的片斷,
<sonnet type="Shakespearean"> 
<author> 
<last-name>Shakespeare</last-name> 
下列是從解析器返回的節點:

Document 節點 

 
Element 節點對應於 <sonnet> 標記 
一個 Text 節點對應於 <sonnet> 節點後的回車符以及 <author> 標記前的兩個空格符 
Element 節點對應於 <author> 標記 
一個 Text 節點對應於 <author> 節點後的回車符以及 <last-name> 標記前的四個空格符 
Element 節點對應於 <last-name> 標記 

所有那些文本節點 

如果您查看由解析器返回的所有節點列表,您將發現它們大多數是沒用的。在每行開始的空格符組成其中包含可忽略的 Text 節點。

注意如果您將所有的節點放在一行上我們就不會得到這些無用的節點了。我們通過添加分行符和空格符來提高文檔的可讀性。

當您構建一個 XML 文檔時不需要考慮可讀性,就可省略分行符和空格符。這可使得您的文檔更小,處理您的文檔時也不需要構建那些無用的節點。


所有那些文本節點 

如果您查看由解析器返回的所有節點列表,您將發現它們大多數是沒用的。在每行開始的空格符組成其中包含可忽略的 Text 節點。

注意如果您將所有的節點放在一行上我們就不會得到這些無用的節點了。我們通過添加分行符和空格符來提高文檔的可讀性。

當您構建一個 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 的不同處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章