明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

Java XML圖文詳細(xì)教程(1-3章)

[摘要]來(lái)源:http://d23xapp2.cn.ibm.com/developerWorks/education/xml/xmljava/tutorial/xmljava-1-1.html第一章 入門介...
來(lái)源:http://d23xapp2.cn.ibm.com/developerWorks/education/xml/xmljava/tutorial/xmljava-1-1.html

第一章 入門介紹

關(guān)于本教程
在本教程中,我們將討論如何使用一個(gè) XML 解析器來(lái):
處理一個(gè) XML 文檔
創(chuàng)建一個(gè) XML 文檔
操作一個(gè) XML 文檔
我們也將討論一些有用而不為眾人所知的 XML 解析器特性。 最重要的,我們所討論的每個(gè)工具都可從 IBM 的 alphaWorks 站點(diǎn) (www.alphaworks.ibm.com) 和其它網(wǎng)站免費(fèi)獲得。

未討論的:

有些重要的編程概念并未在此介紹:
1 使用可視工具來(lái)構(gòu)建 XML 應(yīng)用
2 將一個(gè) XML 文檔從一種形式轉(zhuǎn)換到另一種
3 為最終用戶或其他進(jìn)程創(chuàng)建接口,及對(duì)后端存儲(chǔ)數(shù)據(jù)的接口
當(dāng)您構(gòu)建一個(gè) XML 應(yīng)用時(shí),所有這些概念都是重要的。我們正在編制新的教程來(lái)討論它們,因此請(qǐng)常光顧我們的網(wǎng)址!

XML 應(yīng)用架構(gòu)

一個(gè) XML 應(yīng)用通常是基于一個(gè) XML 解析器而構(gòu)建的。它為其用戶提供了一個(gè)接口,以及對(duì)后端存儲(chǔ)數(shù)據(jù)的一個(gè)接口。

本教程關(guān)注于編寫使用 XML 解析器來(lái)操作 XML 文檔的 Java 代碼。如下邊圖片所示,本教程關(guān)注于中間那塊。







第二章 解析器基礎(chǔ)

基礎(chǔ)

一個(gè) XML 解析器是一段可以讀入一個(gè)文檔并分析其結(jié)構(gòu)的代碼。在本章節(jié),我們將討論如何使用一個(gè) XML 解析器來(lái)讀入一個(gè) XML 文檔。我們也將討論不同類型的解析器以及您在何時(shí)使用它們。

本教程后面的章節(jié)將討論您從解析器可以獲得什么以及如何使用這些結(jié)果。

如何使用一個(gè)解析器

我們將在稍后的章節(jié)詳細(xì)討論它,但通常而言,您如下使用它:

1 創(chuàng)建一個(gè)解析器對(duì)象
2 將您的 XML 文檔傳遞給解析器
3 處理結(jié)果
構(gòu)建一個(gè) XML 應(yīng)用顯然遠(yuǎn)遠(yuǎn)超出這些,但通常一個(gè) XML 的應(yīng)用將包含這些流程。

解析器種類

有不同的方法來(lái)劃分解析器種類:

驗(yàn)證或非驗(yàn)證解析器
支持 Document Object Model (DOM) 的解析器
支持 Simple API for XML (SAX) 的解析器
用特定語(yǔ)言編寫的解析器 (Java, C++, Perl 等)

驗(yàn)證或非驗(yàn)證解析器

如我們?cè)诘谝粋(gè)教程中所提及的,XML 文檔如果使用一個(gè) DTD 并符合 DTD 中的規(guī)則將被稱為有效文檔(valid document)。符合基本標(biāo)記規(guī)則的 XML 文檔被稱為格式正確文檔(well-formed document)。

XML 規(guī)范要求所有的解析器當(dāng)其發(fā)現(xiàn)一個(gè)文檔不是格式正確時(shí)要報(bào)錯(cuò)。驗(yàn)證(Validation)則是另一個(gè)問題了。驗(yàn)證解析器(Validating parser)在解析 XML 文檔同時(shí)進(jìn)行驗(yàn)證。非驗(yàn)證解析器(Non-validating parser) 忽略所有的驗(yàn)證錯(cuò)誤。換而言之,如果一個(gè) XML 文檔是格式正確的時(shí),一個(gè)非驗(yàn)證解析器并不關(guān)注文檔是否符合其對(duì)應(yīng) DTD 所指定的規(guī)則(如果有的話)。

為何使用非驗(yàn)證解析器?

速度和效率。要一個(gè) XML 解析器處理 DTD 并確保每個(gè) XML 的元素符合在 DTD 中的規(guī)則需要相當(dāng)大的開銷。如果您確定一個(gè) XML 文檔是有效的(可能來(lái)自一個(gè)數(shù)據(jù)源),那就沒有必要在次驗(yàn)證它了。

同樣,有時(shí)您所需要的只是從一個(gè)文檔中找出 XML 的標(biāo)記。一旦您有了這些標(biāo)記,您可以將數(shù)據(jù)從中提取出然后加以處理。如果這就是您所需要的,一個(gè)非驗(yàn)證解析器就是正確的選擇。

Document Object Model (DOM)

文檔對(duì)象模型(Document Object Model)是 World Wide Web Consortium(W3C) 的正式推薦。它定義了一個(gè)接口使得程序可以存取和更新 XML 文檔的風(fēng)格、結(jié)構(gòu)和內(nèi)容。支持 DOM 的 XML 解析器實(shí)現(xiàn)該接口。

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

DOM 解析器可提供什么

當(dāng)您用一個(gè) DOM 解析器來(lái)解析一個(gè) XML 文檔時(shí),您將獲得一個(gè)包含文檔中所有元素的樹結(jié)構(gòu)。DOM 提供了不同的功能來(lái)檢查文檔的內(nèi)容和結(jié)構(gòu)。

關(guān)于標(biāo)準(zhǔn)

現(xiàn)在我們即將討論開發(fā) XML 應(yīng)用了,我們也要關(guān)注 XML 的標(biāo)準(zhǔn)。正式而言,XML 是 MIT(麻省理工) 的商標(biāo)和 World Wide Web Consortium (W3C)組織的產(chǎn)品。

XML 規(guī)范,W3C 的正式推薦,可從 www.w3.org/TR/REC-xml 下載。W3C 站點(diǎn)包含了 XML、DOM 以及一大堆 XML 相關(guān)標(biāo)準(zhǔn)的規(guī)范。

Simple API for XML (SAX)

SAX API 是另一種處理 XML 文檔內(nèi)容的方法。一個(gè)既成事實(shí)的標(biāo)準(zhǔn),它由 David Megginson 和 XML-Dev 郵件列表其它成員所開發(fā)。

要查看完整的 SAX 標(biāo)準(zhǔn),參見 www.megginson.com/SAX/。要參加 XML-Dev 郵件列表,發(fā)送郵件到 majordomo@ic.ac.uk 其中包含: subscribe xml-dev。

SAX 解析器可提供什么

當(dāng)您使用 SAX 解析器來(lái)解析 XML 文檔時(shí),解析器在文檔的不同處將產(chǎn)生事件。由您來(lái)決定對(duì)每個(gè)事件如何處理。

SAX 解析器會(huì)在以下情況下產(chǎn)生事件:在文檔開始和結(jié)束時(shí),在一個(gè)元素開始和結(jié)束時(shí),或者它在一個(gè)元素中找到字符時(shí),以及其它若干點(diǎn)。您可編寫 Java 代碼來(lái)處理每個(gè)事件,以及如何處理從解析器獲得的信息。

何時(shí)使用 SAX?何時(shí)使用 DOM?

我們將在稍后的章節(jié)詳細(xì)討論這個(gè)問題,但通常而言,您在下列時(shí)候應(yīng)該使用一個(gè) DOM 解析器:

您需要十分了解文檔的結(jié)構(gòu)
您需要操作文檔中的某些部分(例如,您可能想對(duì)某些元素排序)
您需要不止一次使用文檔中的信息
當(dāng)您只需要從一個(gè) XML 文檔中提取若干元素時(shí),可使用 SAX 解析器。SAX 解析器在您沒有大多數(shù)內(nèi)存時(shí)、或者如果您只需要使用文檔中的信息一次(而不是解析文檔一次,而后要反復(fù)使用它)。

不同語(yǔ)言的 XML 解析器

在 Web 上使用的大多數(shù)語(yǔ)言都有其對(duì)應(yīng)的 XML 解析器和庫(kù),包括 Java、C++、Perl 和 Python。下一頁(yè)介紹了 IBM 或其它公司提供的解析器的鏈接。

本教程中絕大多數(shù)的示例是使用 IBM 的 XML4J 解析器。我們所討論的所有代碼使用標(biāo)準(zhǔn)的接口。在本教程的最后章節(jié),我們將向您展現(xiàn)編寫可使用不同解析器的代碼是如何簡(jiǎn)單。

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 的會(huì)員)下載。
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 語(yǔ)言的 XML 解析器。要獲得更多信息,參見 www.perlxml.com/faq/perl-xml-faq.html。


Python

要獲得更多 Python 語(yǔ)言的 XML 解析器,參見 www.python.org/topics/xml/。

總結(jié)

任何 XML 應(yīng)用的核心是一個(gè) XML 解析器。要處理一個(gè) XML 文檔,您的應(yīng)用將創(chuàng)建一個(gè) parser 對(duì)象,將一個(gè) XML document 傳遞給它,然后處理從 parser 對(duì)象返回的結(jié)果。

我們討論了不同類型的 XML 解析器,以及您為何選取其一。我們用不同方式來(lái)對(duì)解析器分類:

驗(yàn)證或非驗(yàn)證解析器
支持 Document Object Model (DOM) 的解析器
支持 Simple API for XML (SAX) 的解析器
用特定語(yǔ)言編寫的解析器 (Java, C++, Perl 等)
在我們的下一章節(jié),我們將探討 DOM 解析器和如何使用它們。

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

DOM 是一個(gè)操作文檔結(jié)構(gòu)的通用接口。它設(shè)計(jì)的一個(gè)目標(biāo)是為一個(gè) DOM 兼容解析器所編寫的 Java 代碼應(yīng)該可以使用其它任意 DOM 兼容的解析器而不需要修改代碼。(我們稍后將展示這個(gè)。)

正如我們前面所提的,一個(gè) DOM 解析器將以樹形式返回您整個(gè)文檔的結(jié)構(gòu)。

示例代碼

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

DOM 接口

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

Node: DOM 基本的數(shù)據(jù)類型。
Element: 您將最主要處理的對(duì)象是 Element。
Attr: 代表一個(gè)元素的屬性。
Text: 一個(gè) Element 或 Attr 的實(shí)際內(nèi)容。
Document: 代表整個(gè) XML 文檔。一個(gè) Document 對(duì)象通常也被稱為一棵 DOM 樹。

常用的 DOM 方法

當(dāng)您使用 DOM 時(shí),下列是您將常會(huì)使用的方法:

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

我們的第一個(gè) DOM 應(yīng)用!

介紹了很多概念,讓我們繼續(xù)吧。我們的第一個(gè)應(yīng)用簡(jiǎn)單地讀入一個(gè) XML 文檔并將其內(nèi)容輸出到標(biāo)準(zhǔn)輸出。

在一個(gè)命令行窗口,運(yùn)行下面的命令:

java domOne sonnet.xml

這個(gè)命令將載入我們的應(yīng)用然后讓它解析 sonnet.xml 文件。如果一切運(yùn)行正常,您將看到 XML 文檔的內(nèi)容被輸出到標(biāo)準(zhǔn)輸出。
<?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 的源碼是非常直了的。我們創(chuàng)建一個(gè)新的類 domOne;它有兩個(gè)方法,parseAndPrint 以及 printDOMTree。

在 main 方法中,我們處理命令行,創(chuàng)建一個(gè) domOne 對(duì)象,然后將文件名傳遞給 domOne 對(duì)象。domOne 對(duì)象創(chuàng)建一個(gè) parser 對(duì)象,解析文檔,然后通過 printDOMTree 方法處理 DOM 樹 (即 Document 對(duì)象)。

我們將詳細(xì)研究每個(gè)步驟。
public class domOne
{
public void parseAndPrint(String uri)
...
public void printDOMTree(Node node)
...
public static void main(String argv[])
...

處理命令行

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

我們使用命令行參數(shù)來(lái)簡(jiǎn)化我們的示例。在大多數(shù)情況下,一個(gè) XML 應(yīng)用可能使用 servlet、Java Bean 和其它類型的組件一起使用;而用命令行參數(shù)并不是一個(gè)問題。

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]);
}
創(chuàng)建一個(gè) domOne 對(duì)象

在我們的示例代碼中,我們創(chuàng)建一個(gè)單獨(dú)的類 domOne。要解析文件和打印結(jié)果,我們創(chuàng)建一個(gè) domOne 類的實(shí)例,然后讓我們剛創(chuàng)建的 domOne 對(duì)象來(lái)解析和打印 XML 文檔。

我們?yōu)楹芜@樣處理?由于我們想要使用一個(gè)遞歸方法來(lái)遍歷 DOM 樹并打印出結(jié)果。我們無(wú)法用一個(gè)如 main 的靜態(tài)方法來(lái)處理,因此我們創(chuàng)建一個(gè)單獨(dú)的類來(lái)處理它。


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]);
}
創(chuàng)建一個(gè) Parser 對(duì)象

現(xiàn)在我們已經(jīng)讓 domOne 的實(shí)例來(lái)解析和處理我們的 XML 文檔,它的第一個(gè)處理是創(chuàng)建一個(gè)新的 Parser 對(duì)象。在此例中,我們將使用一個(gè) DOMParser 對(duì)象,一個(gè)實(shí)現(xiàn) DOM 接口的 Java 類。在 XML4J 包中還有其它 parser 對(duì)象,例如 SAXParser、ValidatingSAXParser 和 NonValidatingDOMParser。

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

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

解析 XML 文檔

解析文檔只是簡(jiǎn)單的一行代碼。當(dāng)解析結(jié)束時(shí),我們獲得解析器生成的 Document 對(duì)象。

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

try
{
DOMParser parser = new DOMParser();
parser.parse(uri);
doc = parser.getDocument();
...
if (doc != null)
printDOMTree(doc);
}

處理 DOM 樹

現(xiàn)在解析已經(jīng)完成,我們將遍歷 DOM 樹。注意這段代碼是遞歸的。對(duì)每個(gè)節(jié)點(diǎn),我們處理其本身,然后我們對(duì)每個(gè)節(jié)點(diǎn)的子女遞歸地調(diào)用 printDOMTree 方法。遞歸調(diào)用如左所示。

要記住當(dāng)有些 XML 文檔非常大時(shí),它們反而不會(huì)有太多層標(biāo)記。以一個(gè)上海市的電話簿為例,可能有幾百萬(wàn)條記錄,但其標(biāo)記可能不會(huì)超過幾層?紤]到這個(gè)原因,遞歸算法的棧溢出不是一個(gè)問題。

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,有二十四個(gè)節(jié)點(diǎn)。您可能認(rèn)為這意味著二十四個(gè)節(jié)點(diǎn)。然而,這不正確。在 sonnet.xml 中一共有 69 個(gè)節(jié)點(diǎn);一個(gè)文檔節(jié)點(diǎn)(document node), 23 個(gè)元素節(jié)點(diǎn)(element node)以及 45 個(gè)文本節(jié)點(diǎn)(text node)。我們運(yùn)行 java domCounter sonnet.xml 就獲得了下邊所示的結(jié)果。

domCounter.java

這段代碼解析一個(gè) XML 文檔,然后遍歷 DOM 樹來(lái)采集有關(guān)該文檔的數(shù)據(jù)。當(dāng)數(shù)據(jù)采集后將其輸出到標(biāo)準(zhǔn)輸出。


統(tǒng)計(jì) sonnet.xml 的數(shù)據(jù):
====================================
Document Nodes: 1
Element Nodes: 23
Entity Reference Nodes: 0
CDATA Sections: 0
Text Nodes: 45
Processing Instructions: 0
----------
Total: 69 Nodes

節(jié)點(diǎn)列表示例

對(duì)于下邊的片斷,
<sonnet type="Shakespearean">
<author>
<last-name>Shakespeare</last-name>
下列是從解析器返回的節(jié)點(diǎn):

Document 節(jié)點(diǎn)


Element 節(jié)點(diǎn)對(duì)應(yīng)于 <sonnet> 標(biāo)記
一個(gè) Text 節(jié)點(diǎn)對(duì)應(yīng)于 <sonnet> 節(jié)點(diǎn)后的回車符以及 <author> 標(biāo)記前的兩個(gè)空格符
Element 節(jié)點(diǎn)對(duì)應(yīng)于 <author> 標(biāo)記
一個(gè) Text 節(jié)點(diǎn)對(duì)應(yīng)于 <author> 節(jié)點(diǎn)后的回車符以及 <last-name> 標(biāo)記前的四個(gè)空格符
Element 節(jié)點(diǎn)對(duì)應(yīng)于 <last-name> 標(biāo)記

所有那些文本節(jié)點(diǎn)

如果您查看由解析器返回的所有節(jié)點(diǎn)列表,您將發(fā)現(xiàn)它們大多數(shù)是沒用的。在每行開始的空格符組成其中包含可忽略的 Text 節(jié)點(diǎn)。

注意如果您將所有的節(jié)點(diǎn)放在一行上我們就不會(huì)得到這些無(wú)用的節(jié)點(diǎn)了。我們通過添加分行符和空格符來(lái)提高文檔的可讀性。

當(dāng)您構(gòu)建一個(gè) XML 文檔時(shí)不需要考慮可讀性,就可省略分行符和空格符。這可使得您的文檔更小,處理您的文檔時(shí)也不需要構(gòu)建那些無(wú)用的節(jié)點(diǎn)。


所有那些文本節(jié)點(diǎn)

如果您查看由解析器返回的所有節(jié)點(diǎn)列表,您將發(fā)現(xiàn)它們大多數(shù)是沒用的。在每行開始的空格符組成其中包含可忽略的 Text 節(jié)點(diǎn)。

注意如果您將所有的節(jié)點(diǎn)放在一行上我們就不會(huì)得到這些無(wú)用的節(jié)點(diǎn)了。我們通過添加分行符和空格符來(lái)提高文檔的可讀性。

當(dāng)您構(gòu)建一個(gè) XML 文檔時(shí)不需要考慮可讀性,就可省略分行符和空格符。這可使得您的文檔更小,處理您的文檔時(shí)也不需要構(gòu)建那些無(wú)用的節(jié)點(diǎn)。

<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>

一個(gè) Text 節(jié)點(diǎn)對(duì)應(yīng)于 "Shakespeare" 字符
如果您看到標(biāo)記間所有的空格符,您可發(fā)現(xiàn)為何我們有那么多超出您想像的節(jié)點(diǎn)。

了解您的 Node

我們最后對(duì)處理在 DOM 樹的 Node 要指出的是,我們?cè)谔幚砥渲耙獧z查每個(gè) Node 的類型。一些方法,例如 getAttributes,對(duì)一些特定的節(jié)點(diǎn)類型返回 null 值。如果您不檢查節(jié)點(diǎn)類型,您將得到不正確的結(jié)果(最佳情況)和異常(最差情況)。

在此所介紹的 switch 語(yǔ)句常出現(xiàn)在使用 DOM 解析器的代碼。
switch (nodeType)
{
case Node.DOCUMENT_NODE:
...
case Node.ELEMENT_NODE:
...
case Node.TEXT_NODE:
...
}


總結(jié)

不管您信不信,這就是我們使用 DOM 對(duì)象所要了解的所有內(nèi)容。我們的 domOne 代碼完成了下列工作:

創(chuàng)建一個(gè) Parser 對(duì)象
將一個(gè) XML 文檔傳遞給 Parser 來(lái)解析
獲得來(lái)自于 Parser 的 Document 對(duì)象然后加以檢查。
在本教程最后一章,我們將討論如何不需要 XML 原文件來(lái)構(gòu)建一棵 DOM 樹,并展示如何對(duì)一個(gè) XML 文檔中的元素排序。而那些都是基于我們這里所討論的概念之上。

在我們繼續(xù)那些更高級(jí)的應(yīng)用前,我們將詳細(xì)探討 SAX API。我們同樣將使用類似的示例,展現(xiàn) SAX 和 DOM 的不同處。