版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、Java解析XML文件的DOM和SAX方式 35/35Java解析XML文件的DOM和SAX方式Java解析XML文件之:DOMDOM初步 DOM是Document Object Model的縮寫,即文檔對(duì)象模型。前面說過,XML將數(shù)據(jù)組織為一顆樹,所以DOM就是對(duì)這顆樹的一個(gè)對(duì)象描敘。通俗的說,就是通過解析XML文檔,為 XML文檔在邏輯上建立一個(gè)樹模型,樹的節(jié)點(diǎn)是一個(gè)個(gè)對(duì)象。我們通過存取這些對(duì)象就能夠存取XML文檔的內(nèi)容。 下面我們來看一個(gè)簡(jiǎn)單的例子,看看在DOM中,我們是如何來操作一個(gè)XML文檔的。 這是一個(gè)XML文檔,也是我們要操作的對(duì)象: Good-bye serialization
2、, hello Java!下面,我們需要把這個(gè)文檔的內(nèi)容解析到一個(gè)個(gè)的Java對(duì)象中去供程序使用,利用JAXP,我們只需幾行代碼就能做到這一點(diǎn)。首先,我們需要建立一個(gè)解析器工廠,以利用這個(gè)工廠來獲得一個(gè)具體的解析器對(duì)象: DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();我們?cè)谶@里使用DocumentBuilderFacotry的目的是為了創(chuàng)建與具體解析器無(wú)關(guān)的程序,當(dāng)DocumentBuilderFactory類 的靜態(tài)方法newInstance()被調(diào)用時(shí),它根據(jù)一個(gè)系統(tǒng)變量來決定具體使用哪一個(gè)解析器。又因?yàn)?/p>
3、所有的解析器都服從于JAXP所定義的接口,所以 無(wú)論具體使用哪一個(gè)解析器,代碼都是一樣的。所以當(dāng)在不同的解析器之間進(jìn)行切換時(shí),只需要更改系統(tǒng)變量的值,而不用更改任何代碼。這就是工廠所帶來的好 處。這個(gè)工廠模式的具體實(shí)現(xiàn),可以參看下面的類圖。 DocumentBuilder db = dbf.newDocumentBuilder();當(dāng)獲得一個(gè)工廠對(duì)象后,使用它的靜態(tài)方法newDocumentBuilder()方法可以獲得一個(gè)DocumentBuilder對(duì)象,這個(gè)對(duì)象代表了具體的DOM解析器。但具體是哪一種解析器,微軟的或者IBM的,對(duì)于程序而言并不重要。 然后,我們就可以利用這個(gè)解析器來對(duì)X
4、ML文檔進(jìn)行解析了: Document doc = db.parse(c:/xml/message.xml);DocumentBuilder 的parse()方法接受一個(gè)XML文檔名作為輸入?yún)?shù),返回一個(gè)Document對(duì)象,這個(gè)Document對(duì)象就代表了一個(gè)XML文檔的樹模型。以后 所有的對(duì)XML文檔的操作,都與解析器無(wú)關(guān),直接在這個(gè)Document對(duì)象上進(jìn)行操作就可以了。而具體對(duì)Document操作的方法,就是由DOM所定 義的了。 Jaxp支持W3C所推薦的DOM 2。如果你對(duì)DOM很熟悉,那么下面的內(nèi)容就很簡(jiǎn)單了:只需要按照DOM的規(guī)范來進(jìn)行方法調(diào)用就可以。當(dāng)然,如果你對(duì)DOM不清楚
5、,也不用著急,后面我們 會(huì)有詳細(xì)的介紹。在這兒,你所要知道并牢記的是:DOM是用來描敘XML文檔中的數(shù)據(jù)的模型,引入DOM的全部原因就是為了用這個(gè)模型來操作XML文檔的 中的數(shù)據(jù)。DOM規(guī)范中定義有節(jié)點(diǎn)(即對(duì)象)、屬性和方法,我們通過這些節(jié)點(diǎn)的存取來存取XML的數(shù)據(jù)。 從上面得到的 Document對(duì)象開始,我們就可以開始我們的DOM之旅了。使用Document對(duì)象的getElementsByTagName()方法,我們可以 得到一個(gè)NodeList對(duì)象,一個(gè)Node對(duì)象代表了一個(gè)XML文檔中的一個(gè)標(biāo)簽元素,而NodeList對(duì)象,觀其名而知其意,所代表的是一個(gè) Node對(duì)象的列表: Node
6、List nl = doc.getElementsByTagName(message);我們通過這樣一條語(yǔ)句所得到的是XML文檔中所有標(biāo)簽對(duì)應(yīng)的Node對(duì)象的一個(gè)列表。然后,我們可以使用NodeList對(duì)象的item()方法來得到列表中的每一個(gè)Node對(duì)象: Node my_node = nl.item(0);當(dāng)一個(gè)Node對(duì)象被建立之后,保存在XML文檔中的數(shù)據(jù)就被提取出來并封裝在這個(gè)Node中了。在這個(gè)例子中,要提取Message標(biāo)簽內(nèi)的內(nèi)容,我們通常會(huì)使用Node對(duì)象的getNodeValue()方法: String message = my_node.getFirstChild().g
7、etNodeValue();請(qǐng) 注意,這里還使用了一個(gè)getFirstChild()方法來獲得message下面的第一個(gè)子Node對(duì)象。雖然在message標(biāo)簽下面除了文本外并 沒有其它子標(biāo)簽或者屬性,但是我們堅(jiān)持在這里使用getFirseChild()方法,這主要和W3C對(duì)DOM的定義有關(guān)。W3C把標(biāo)簽內(nèi)的文本部分也定 義成一個(gè)Node,所以先要得到代表文本的那個(gè)Node,我們才能夠使用getNodeValue()來獲取文本的內(nèi)容。 現(xiàn)在,既然我們已經(jīng)能夠從XML文件中提取出數(shù)據(jù)了,我們就可以把這些數(shù)據(jù)用在合適的地方,來構(gòu)筑應(yīng)用程序。 下面的內(nèi)容,我們將更多的關(guān)注DOM,為DOM作一個(gè)較為詳
8、細(xì)的解析,使我們使用起來更為得心應(yīng)手。 DOM詳解 1基本的DOM對(duì)象DOM的基本對(duì)象有5個(gè):Document,Node,NodeList,Element和Attr。下面就這些對(duì)象的功能和實(shí)現(xiàn)的方法作一個(gè)大致的介紹。 Document對(duì)象代表了整個(gè)XML的文檔,所有其它的Node,都以一定的順序包含在Document對(duì)象之內(nèi),排列成一個(gè)樹形的結(jié) 構(gòu),程序員可以通過遍歷這顆樹來得到XML文檔的所有的內(nèi)容,這也是對(duì)XML文檔操作的起點(diǎn)。我們總是先通過解析XML源文件而得到一個(gè)Document 對(duì)象,然后再來執(zhí)行后續(xù)的操作。此外,Document還包含了創(chuàng)建其它節(jié)點(diǎn)的方法,比如createAttri
9、but()用來創(chuàng)建一個(gè)Attr對(duì)象。它所包含的主要的方法有: createAttribute(String):用給定的屬性名創(chuàng)建一個(gè)Attr對(duì)象,并可在其后使用setAttributeNode方法來放置在某一個(gè)Element對(duì)象上面。 createElement(String):用給定的標(biāo)簽名創(chuàng)建一個(gè)Element對(duì)象,代表XML文檔中的一個(gè)標(biāo)簽,然后就可以在這個(gè)Element對(duì)象上添加屬性或進(jìn)行其它的操作。 createTextNode(String):用給定的字符串創(chuàng)建一個(gè)Text對(duì)象,Text對(duì)象代表了標(biāo)簽或者屬性中所包含的純文本字符串。如果在一個(gè)標(biāo)簽內(nèi)沒有其它的標(biāo)簽,那么標(biāo)簽內(nèi)的文本所
10、代表的Text對(duì)象是這個(gè)Element對(duì)象的唯一子對(duì)象。 getElementsByTagName(String):返回一個(gè)NodeList對(duì)象,它包含了所有給定標(biāo)簽名字的標(biāo)簽。 getDocumentElement():返回一個(gè)代表這個(gè)DOM樹的根節(jié)點(diǎn)的Element對(duì)象,也就是代表XML文檔根元素的那個(gè)對(duì)象。 Node 對(duì)象是DOM結(jié)構(gòu)中最為基本的對(duì)象,代表了文檔樹中的一個(gè)抽象的節(jié)點(diǎn)。在實(shí)際使用的時(shí)候,很少會(huì)真正的用到Node這個(gè)對(duì)象,而是用到諸如 Element、Attr、Text等Node對(duì)象的子對(duì)象來操作文檔。Node對(duì)象為這些對(duì)象提供了一個(gè)抽象的、公共的根。雖然在Node對(duì)象中定義
11、了 對(duì)其子節(jié)點(diǎn)進(jìn)行存取的方法,但是有一些Node子對(duì)象,比如Text對(duì)象,它并不存在子節(jié)點(diǎn),這一點(diǎn)是要注意的。Node對(duì)象所包含的主要的方法有: appendChild(org.w3c.dom.Node):為這個(gè)節(jié)點(diǎn)添加一個(gè)子節(jié)點(diǎn),并放在所有子節(jié)點(diǎn)的最后,如果這個(gè)子節(jié)點(diǎn)已經(jīng)存在,則先把它刪掉再添加進(jìn)去。 getFirstChild():如果節(jié)點(diǎn)存在子節(jié)點(diǎn),則返回第一個(gè)子節(jié)點(diǎn),對(duì)等的,還有g(shù)etLastChild()方法返回最后一個(gè)子節(jié)點(diǎn)。 getNextSibling():返回在DOM樹中這個(gè)節(jié)點(diǎn)的下一個(gè)兄弟節(jié)點(diǎn),對(duì)等的,還有g(shù)etPreviousSibling()方法返回其前一個(gè)兄弟節(jié)點(diǎn)。
12、getNodeName():根據(jù)節(jié)點(diǎn)的類型返回節(jié)點(diǎn)的名稱。 getNodeType():返回節(jié)點(diǎn)的類型。 getNodeValue():返回節(jié)點(diǎn)的值。 hasChildNodes():判斷是不是存在有子節(jié)點(diǎn)。 hasAttributes():判斷這個(gè)節(jié)點(diǎn)是否存在有屬性。 getOwnerDocument():返回節(jié)點(diǎn)所處的Document對(duì)象。 insertBefore(org.w3c.dom.Node new,org.w3c.dom.Node ref):在給定的一個(gè)子對(duì)象前再插入一個(gè)子對(duì)象。 removeChild(org.w3c.dom.Node):刪除給定的子節(jié)點(diǎn)對(duì)象。 replaceC
13、hild(org.w3c.dom.Node new,org.w3c.dom.Node old):用一個(gè)新的Node對(duì)象代替給定的子節(jié)點(diǎn)對(duì)象。 NodeList對(duì)象,顧名思義,就是代表了一個(gè)包含了一個(gè)或者多個(gè)Node的列表??梢院?jiǎn)單的把它看成一個(gè)Node的數(shù)組,我們可以通過方法來獲得列表中的元素: getLength():返回列表的長(zhǎng)度。 item(int):返回指定位置的Node對(duì)象。 Element對(duì)象代表的是XML文檔中的標(biāo)簽元素,繼承于Node,亦是Node的最主要的子對(duì)象。在標(biāo)簽中可以包含有屬性,因而Element對(duì)象中有存取其屬性的方法,而任何Node中定義的方法,也可以用在Elem
14、ent對(duì)象上面。 getElementsByTagName(String):返回一個(gè)NodeList對(duì)象,它包含了在這個(gè)標(biāo)簽中其下的子孫節(jié)點(diǎn)中具有給定標(biāo)簽名字的標(biāo)簽。getTagName():返回一個(gè)代表這個(gè)標(biāo)簽名字的字符串。 getAttribute(String): 返回標(biāo)簽中給定屬性名稱的屬性的值。在這兒需要主要的是,應(yīng)為XML文檔中允許有實(shí)體屬性出現(xiàn),而這個(gè)方法對(duì)這些實(shí)體屬性并不適用。這時(shí)候需要用到 getAttributeNodes()方法來得到一個(gè)Attr對(duì)象來進(jìn)行進(jìn)一步的操作。 getAttributeNode(String):返回一個(gè)代表給定屬性名稱的Attr對(duì)象。 Attr
15、對(duì)象代表了某個(gè)標(biāo)簽中的屬性。Attr繼承于Node,但是因?yàn)锳ttr實(shí)際上是包含在Element中的,它并不能被看作是Element的子對(duì)象,因 而在DOM中Attr并不是DOM樹的一部分,所以Node中的getparentNode(),getpreviousSibling()和 getnextSibling()返回的都將是null。也就是說,Attr其實(shí)是被看作包含它的Element對(duì)象的一部分,它并不作為DOM樹中單獨(dú) 的一個(gè)節(jié)點(diǎn)出現(xiàn)。這一點(diǎn)在使用的時(shí)候要同其它的Node子對(duì)象相區(qū)別。 需要說明的是,上面所說的DOM對(duì)象在DOM中都是用接口定義 的,在定義的時(shí)候使用的是與具體語(yǔ)言無(wú)關(guān)的ID
16、L語(yǔ)言來定義的。因而,DOM其實(shí)可以在任何面向?qū)ο蟮恼Z(yǔ)言中實(shí)現(xiàn),只要它實(shí)現(xiàn)了DOM所定義的接口和功能 就可以了。同時(shí),有些方法在DOM中并沒有定義,是用IDL的屬性來表達(dá)的,當(dāng)被映射到具體的語(yǔ)言時(shí),這些屬性被映射為相應(yīng)的方法。 2DOM實(shí)例有了上面的介紹,相信你對(duì)DOM理解的更多了吧。下面的例子將讓你對(duì)DOM更加熟悉起來。 先說說這個(gè)例子到底要做的是什么吧,我們希望在一個(gè)名為link.xml文件中保存了一些URL地址,通過一個(gè)簡(jiǎn)單的程序,我們可以通過DOM把這些URL 讀出并顯示出來,也可以反過來向這個(gè)XML文件中寫入加入的URL地址。很簡(jiǎn)單,卻很實(shí)用,也足夠來例示DOM的絕大部分用法了。 X
17、ML文件本身不復(fù)雜,就不給出它的DTD了。link.xml: JSP InsiderJSP Insider212001A JSP information site.The makers of JavaSun Microsystems312001Sun Microsystems website.The standard JSP containerApache Group412001Some great software.第一個(gè)程序我們稱為Xmldisplay.java,主要的功能就是讀取這個(gè)XML文件中各個(gè)節(jié)點(diǎn)的內(nèi)容,然后在格式化輸出在System.out上,我們來看看這個(gè)程序: import
18、javax.xml.parsers.*;import org.w3c.dom.*;這是引入必要的類,因?yàn)樵谶@里使用的是Sun所提供的XML解析器,因而需要引入java.xml.parsers包,其中包含了有DOM解析器和SAX解析器的具體實(shí)現(xiàn)。org.w3c.dom包中定義了w3c所制定的DOM接口。 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder=factory.newDocumentBuilder();Document doc=builder.pars
19、e(links.xml);doc.normalize();除 了上面講到的,還有一個(gè)小技巧,對(duì)Document對(duì)象調(diào)用normalize(),可以去掉XML文檔中作為格式化內(nèi)容的空白而映射在DOM樹中的不必 要的Text Node對(duì)象。否則你得到的DOM樹可能并不如你所想象的那樣。特別是在輸出的時(shí)候,這個(gè)normalize()更為有用。 NodeList links =doc.getElementsByTagName(link);剛 才說過,XML文檔中的空白符也會(huì)被作為對(duì)象映射在DOM樹中。因而,直接調(diào)用Node方法的getChildNodes方法有時(shí)候會(huì)有些問題,有時(shí)不能 夠返回所期望的N
20、odeList對(duì)象。解決的辦法是使用Element的getElementByTagName(String),返回的 NodeLise就是所期待的對(duì)象了。然后,可以用item()方法提取想要的元素。 for (int i=0;ilinks.getLength();i+)Element link=(Element) links.item(i);System.out.print(Content: );System.out.println(link.getElementsByTagName(text).item(0).getFirstChild().getNodeValue();System.out.
21、print(URL: );System.out.println(link.getElementsByTagName(url).item(0).getFirstChild().getNodeValue();System.out.print(Author: );System.out.println(link.getElementsByTagName(author).item(0).getFirstChild().getNodeValue();System.out.print(Date: );Element linkdate=(Element) link.getElementsByTagName(d
22、ate).item(0);String day=linkdate.getElementsByTagName(day).item(0).getFirstChild().getNodeValue();String month=linkdate.getElementsByTagName(month).item(0).getFirstChild().getNodeValue();String year=linkdate.getElementsByTagName(year).item(0).getFirstChild().getNodeValue();System.out.println(day+-+m
23、onth+-+year);System.out.print(Description: );System.out.println(link.getElementsByTagName(description).item(0).getFirstChild().getNodeValue();System.out.println();上面的代碼片斷就完成了對(duì)XML文檔內(nèi)容的格式化輸出。只要注意到一些細(xì)節(jié)的問題,比如getFirstChile()方法和getElementsByTagName()方法的使用,這些還是比較容易的。 下面的內(nèi)容,就是在修改了DOM樹后重新寫入到XML文檔中去的問題了。這個(gè)程序名
24、為xmlwrite.java。在JAXP1.0版本中,并沒有直接的類 和方法能夠處理XML文檔的寫入問題,需要借助其它包中的一些輔助類。而在JAXP1.1版本中,引入了對(duì)XSLT的支持,所謂XSLT,就是對(duì)XML文 檔進(jìn)行變換(Translation)后,得到一個(gè)新的文檔結(jié)構(gòu)。利用這個(gè)新加入的功能,我們就能夠很方便的把新生成或者修改后的DOM樹從新寫回到 XML文件中去了,下面我們來看看代碼的實(shí)現(xiàn),這段代碼的主要功能是向links.xml文件中加入一個(gè)新的link節(jié)點(diǎn): import javax.xml.parsers.*;import javax.xml.transform.*;import
25、 javax.xml.transform.dom.DOMSource;import javax.xml.transform.stream.StreamResult;import org.w3c.dom.*;新引入的java.xml.transform包中的幾個(gè)類,就是用來處理XSLT變換的。 我們希望在上面的XML文件中加入一個(gè)新的link節(jié)點(diǎn),因而首先還是要讀入links.xml文件,構(gòu)建一個(gè)DOM樹,然后再對(duì)這個(gè)DOM樹進(jìn)行修改(添加節(jié)點(diǎn)),最后把修改后的DOM寫回到links.xml文件中: DocumentBuilderFactory factory = DocumentBuilder
26、Factory.newInstance();DocumentBuilder builder=factory.newDocumentBuilder();Document doc=builder.parse(links.xml);doc.normalize();/取得變量String text=Hanzhongs Homepage;String url=;String author=Hzliu Liu;String discription=A site from Hanzhong Liu, give u lots of suprise!;為了看清重點(diǎn),簡(jiǎn)化程序,我們把要加入的內(nèi)容硬編碼到記憶Str
27、ing對(duì)象中,而實(shí)際操作中,往往利用一個(gè)界面來提取用戶輸入,或者通過JDBC從數(shù)據(jù)庫(kù)中提取想要的內(nèi)容。 Text textseg;Element link=doc.createElement(link);首 先應(yīng)該明了的是,無(wú)論什么類型的Node,Text型的也好,Attr型的也好,Element型的也好,它們的創(chuàng)建都是通過Document對(duì)象中的 createXXX()方法來創(chuàng)建的(XXX代表具體要?jiǎng)?chuàng)建的類型),因此,我們要向XML文檔中添加一個(gè)link項(xiàng)目,首先要?jiǎng)?chuàng)建一個(gè)link對(duì)象: Element linktext=doc.createElement(text);textseg=doc
28、.createTextNode(text);linktext.appendChild(textseg);link.appendChild(linktext);Element linkurl=doc.createElement(url);textseg=doc.createTextNode(url);linkurl.appendChild(textseg);link.appendChild(linkurl);Element linkauthor=doc.createElement(author);textseg=doc.createTextNode(author);linkauthor.appe
29、ndChild(textseg);link.appendChild(linkauthor);java.util.Calendar rightNow = java.util.Calendar.getInstance();String day=Integer.toString(rightNow.get(java.util.Calendar.DAY_OF_MONTH);String month=Integer.toString(rightNow.get(java.util.Calendar.MONTH);String year=Integer.toString(rightNow.get(java.u
30、til.Calendar.YEAR);Element linkdate=doc.createElement(date);Element linkdateday=doc.createElement(day);textseg=doc.createTextNode(day);linkdateday.appendChild(textseg);Element linkdatemonth=doc.createElement(month);textseg=doc.createTextNode(month);linkdatemonth.appendChild(textseg);Element linkdate
31、year=doc.createElement(year);textseg=doc.createTextNode(year);linkdateyear.appendChild(textseg);linkdate.appendChild(linkdateday);linkdate.appendChild(linkdatemonth);linkdate.appendChild(linkdateyear);link.appendChild(linkdate);Element linkdiscription=doc.createElement(description);textseg=doc.creat
32、eTextNode(discription);linkdiscription.appendChild(textseg);link.appendChild(linkdiscription);創(chuàng) 建節(jié)點(diǎn)的過程可能有些千篇一律,但需要注意的地方是,對(duì)Element中所包含的text(在DOM中,這些text也是代表了一個(gè)Node的,因此也必 須為它們創(chuàng)建相應(yīng)的node),不能直接用Element對(duì)象的setNodeValue()方法來設(shè)置這些text的內(nèi)容,而需要用創(chuàng)建的Text對(duì)象 的setNodeValue()方法來設(shè)置文本,這樣才能夠把創(chuàng)建的Element和其文本內(nèi)容添加到DOM樹中??纯辞懊?/p>
33、的代碼,你會(huì)更好的理解這一 點(diǎn): doc.getDocumentElement().appendChild(link);最后,不要忘記把創(chuàng)建好的節(jié)點(diǎn)添加到DOM樹中。Document類的getDocumentElement()方法,返回代表文檔根節(jié)點(diǎn)的Element對(duì)象。在XML文檔中,根節(jié)點(diǎn)一定是唯一的。 TransformerFactory tFactory =TransformerFactory.newInstance();Transformer transformer = tFactory.newTransformer();DOMSource source = new DOMSourc
34、e(doc);StreamResult result = new StreamResult(new java.io.File(links.xml);transformer.transform(source, result);然 后就是用XSLT把DOM樹輸出了。這里的TransformerFactory也同樣應(yīng)用了工廠模式,使得具體的代碼同具體的變換器無(wú)關(guān)。實(shí)現(xiàn)的方法和 DocumentBuilderFactory相同,這兒就不贅述了。Transformer類的transfrom方法接受兩個(gè)參數(shù)、一個(gè)數(shù)據(jù)源 Source和一個(gè)輸出目標(biāo)Result。這里分別使用的是DOMSource和Strea
35、mResult,這樣就能夠把DOM的內(nèi)容輸出到一個(gè)輸出流中,當(dāng)這個(gè)輸出流是一個(gè)文件的時(shí)候,DOM的內(nèi)容就被寫入到文件中去了。Java解析XML文件之:SAX還是沿用講DOM的時(shí)候使用的那個(gè)文檔例子,但首先,我們先看一個(gè)簡(jiǎn)單一些的應(yīng)用,我們希望能夠統(tǒng)計(jì)一下XML文件中各個(gè)標(biāo)簽出現(xiàn)的次數(shù)。這個(gè)例子很簡(jiǎn)單,但是足以闡述SAX編程的基本思路了。 一開始當(dāng)然還是import語(yǔ)句了: import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.*; import org.xml.sax.*; import org.xml.sax.
36、helpers.*; import java.util.*; import java.io.*; 然后,我們創(chuàng)建一個(gè)繼承于DefaultHandler的類,具體的程序邏輯在這兒可以暫且放在一邊,要注意的是程序的結(jié)構(gòu): public class SAXCounter extends DefaultHandler private Hashtable tags; /這個(gè)Hashtable用來記錄tag出現(xiàn)的次數(shù) / 處理文檔前的工作 public void startDocument() throws SAXException tags = new Hashtable();/初始化Hashtable
37、 /對(duì)每一個(gè)開始元屬進(jìn)行處理 public void startElement(String namespaceURI, String localName, String rawName, Attributes atts) throws SAXException String key = localName; Object value = tags.get(key); if (value = null) / 如果是新碰到的標(biāo)簽,這在Hastable中添加一條記錄 tags.put(key, new Integer(1); else / 如果以前碰到過,得到其計(jì)數(shù)值,并加1 int count
38、= (Integer)value).intValue(); count+; tags.put(key, new Integer(count); /解析完成后的統(tǒng)計(jì)工作 public void endDocument() throws SAXException Enumeration e = tags.keys(); while (e.hasMoreElements() String tag = (String)e.nextElement(); int count = (Integer)tags.get(tag).intValue(); System.out.println(Tag occurs
39、 + count + times); /程序入口,用來完成解析工作 static public void main(String args) String filename = null; boolean validation = false; filename=links.xml; SAXParserFactory spf = SAXParserFactory.newInstance(); XMLReader xmlReader = null; SAXParser saxParser=null; try / 創(chuàng)建一個(gè)解析器SAXParser對(duì)象 saxParser = spf.newSAXP
40、arser(); / 得到SAXParser中封裝的SAX XMLReader xmlReader = saxParser.getXMLReader(); catch (Exception ex) System.err.println(ex); System.exit(1); try /使用指定的ContentHandler,解析給XML文件,這兒要注意的是,為了 /程序的簡(jiǎn)單起見,這兒將主程序和ContentHandler放在了一起。實(shí)際上 /main方法中所作的所有事情,都與ContentHandler無(wú)關(guān)。 xmlReader.parse(new File(filename),new S
41、AXCounter(); catch (SAXException se) System.err.println(se.getMessage(); System.exit(1); catch (IOException ioe) System.err.println(ioe); System.exit(1); 我 們來看看這段程序作了些什么,在main()方法中,主要做的就是創(chuàng)建解析器,然后解析文檔。實(shí)際上,在這兒創(chuàng)建SAXParser對(duì)象的時(shí)候,為了使程 序代碼于具體的解析器無(wú)關(guān),使用了同DOM中一樣的設(shè)計(jì)技巧:通過一個(gè)SAXParserFactory類來創(chuàng)建具體的SAXParser對(duì)象,這樣,
42、當(dāng) 需要使用不同的解析器的時(shí)候,要改變的,只是一個(gè)環(huán)境變量的值,而程序的代碼可以保持不變。這就是FactoryMethod模式的思想。在這兒不再具體 講了,如果還有不明白的,可以參看上面DOM中的解釋,原理是一樣的。 不過在這兒還有一點(diǎn)點(diǎn)要注意的地方,就是SAXParser類 和XMLReader類之間的關(guān)系。你可能有些迷糊了吧,實(shí)際上SAXParser是JAXP中對(duì)XMLReader的一個(gè)封裝類,而XMLReader 是定義在SAX2.0種的一個(gè)用來解析文檔的接口。你可以同樣的調(diào)用SAXParser或者XMLReader中的parser()方法來解析文檔,效果 是完全一樣的。不過在SAXPa
43、rser中的parser()方法接受更多的參數(shù),可以對(duì)不同的XML文檔數(shù)據(jù)源進(jìn)行解析,因而使用起來要比 XMLReader要方便一些。這個(gè)例子僅僅涉及了SAX的一點(diǎn)皮毛,而下面的這個(gè),可就要高級(jí)一些了。下面我們要實(shí)現(xiàn)的功能,在DOM的例子中已經(jīng)有實(shí)現(xiàn)了,就是從XML文檔中讀出內(nèi)容并格式化輸出,雖然程序邏輯看起來還是很簡(jiǎn)單,但是SAX可不比DOM哦,看著吧。 前 面說過,當(dāng)遇到一個(gè)開始標(biāo)簽的時(shí)候,在startElement()方法中,我們并不能夠得到這個(gè)標(biāo)簽在XML文檔中所處的位置。這在處理XML文檔的時(shí) 候是個(gè)大麻煩,因?yàn)樵赬ML中標(biāo)簽的語(yǔ)義,有一部分是由其所處的位置所決定的。而且在一些需要驗(yàn)
44、證文檔結(jié)構(gòu)的程序中,這更是一個(gè)問題。當(dāng)然,沒有解決不了 的問題了,我們可以使用一個(gè)棧來實(shí)現(xiàn)對(duì)文檔結(jié)構(gòu)的紀(jì)錄。 棧的特點(diǎn)是先進(jìn)先出,我們現(xiàn)在的想法是,在startElemnt()方法中 用push將這個(gè)標(biāo)簽的名字添加到棧中,在endElement()方法中在把它pop出來。我們知道對(duì)一個(gè)結(jié)構(gòu)良好的XML而言,其嵌套結(jié)構(gòu)是完備的, 每一個(gè)開始標(biāo)簽總會(huì)對(duì)應(yīng)一個(gè)結(jié)束標(biāo)簽,而且不會(huì)出現(xiàn)標(biāo)簽嵌套之間的錯(cuò)位。因而,每一次startElement()方法的調(diào)用,必然會(huì)對(duì)應(yīng)一個(gè) endElement()方法的調(diào)用,這樣push和pop也是成對(duì)出現(xiàn)的,我們只需要分析棧的結(jié)構(gòu),就可以很容易的知道當(dāng)前標(biāo)簽所處在文檔結(jié)
45、構(gòu)中的位置 了。 public class SAXReader extends DefaultHandler java.util.Stack tags=new java.util.Stack(); /XML Content String text=null; String url=null; String author=null; String description=null; String day=null; String year=null; String month=null; / public void endDocument() throws SAXException System
46、.out.println(Parse End); public void startDocument() throws SAXException System.out.println(Parse Begin); public void startElement(String p0, String p1, String p2, Attributes p3) throws SAXException tags.push(p1); public void endElement(String p0, String p1, String p2) throws SAXException tags.pop()
47、; /一個(gè)link節(jié)點(diǎn)的信息收集齊了,將其格式化輸出 if (p1.equals(link) printout(); public void characters(char p0, int p1, int p2) throws SAXException /從棧中得到當(dāng)前節(jié)點(diǎn)的信息 String tag=(String) tags.peek(); if (tag.equals(text) text=new String(p0,p1,p2); else if (tag.equals(url) url=new String(p0,p1,p2); else if (tag.equals(author)
48、 author=new String(p0,p1,p2); else if (tag.equals(day) day=new String(p0,p1,p2); else if (tag.equals(month) month=new String(p0,p1,p2); else if (tag.equals(year) year=new String(p0,p1,p2); else if (tag.equals(description) year=new String(p0,p1,p2); private void printout() System.out.print(Content: )
49、; System.out.println(text); System.out.print(URL: ); System.out.println(url); System.out.print(Author: ); System.out.println(author); System.out.print(Date: ); System.out.println(day+-+month+-+year); System.out.print(Description: ); System.out.println(description); System.out.println(); static publi
50、c void main(String args) String filename = null; boolean validation = false; filename=links.xml; SAXParserFactory spf = SAXParserFactory.newInstance();這個(gè)例子僅僅涉及了SAX的一點(diǎn)皮毛,而下面的這個(gè),可就要高級(jí)一些了。下面我們要實(shí)現(xiàn)的功能,在DOM的例子中已經(jīng)有實(shí)現(xiàn)了,就是從XML文檔中讀出內(nèi)容并格式化輸出,雖然程序邏輯看起來還是很簡(jiǎn)單,但是SAX可不比DOM哦,看著吧。 前 面說過,當(dāng)遇到一個(gè)開始標(biāo)簽的時(shí)候,在startElement()方法
51、中,我們并不能夠得到這個(gè)標(biāo)簽在XML文檔中所處的位置。這在處理XML文檔的時(shí) 候是個(gè)大麻煩,因?yàn)樵赬ML中標(biāo)簽的語(yǔ)義,有一部分是由其所處的位置所決定的。而且在一些需要驗(yàn)證文檔結(jié)構(gòu)的程序中,這更是一個(gè)問題。當(dāng)然,沒有解決不了 的問題了,我們可以使用一個(gè)棧來實(shí)現(xiàn)對(duì)文檔結(jié)構(gòu)的紀(jì)錄。 棧的特點(diǎn)是先進(jìn)先出,我們現(xiàn)在的想法是,在startElemnt()方法中 用push將這個(gè)標(biāo)簽的名字添加到棧中,在endElement()方法中在把它pop出來。我們知道對(duì)一個(gè)結(jié)構(gòu)良好的XML而言,其嵌套結(jié)構(gòu)是完備的, 每一個(gè)開始標(biāo)簽總會(huì)對(duì)應(yīng)一個(gè)結(jié)束標(biāo)簽,而且不會(huì)出現(xiàn)標(biāo)簽嵌套之間的錯(cuò)位。因而,每一次startElemen
52、t()方法的調(diào)用,必然會(huì)對(duì)應(yīng)一個(gè) endElement()方法的調(diào)用,這樣push和pop也是成對(duì)出現(xiàn)的,我們只需要分析棧的結(jié)構(gòu),就可以很容易的知道當(dāng)前標(biāo)簽所處在文檔結(jié)構(gòu)中的位置 了。 public class SAXReader extends DefaultHandler java.util.Stack tags=new java.util.Stack(); /XML Content String text=null; String url=null; String author=null; String description=null; String day=null; String
53、year=null; String month=null; / public void endDocument() throws SAXException System.out.println(Parse End); public void startDocument() throws SAXException System.out.println(Parse Begin); public void startElement(String p0, String p1, String p2, Attributes p3) throws SAXException tags.push(p1); pu
54、blic void endElement(String p0, String p1, String p2) throws SAXException tags.pop(); /一個(gè)link節(jié)點(diǎn)的信息收集齊了,將其格式化輸出 if (p1.equals(link) printout(); public void characters(char p0, int p1, int p2) throws SAXException /從棧中得到當(dāng)前節(jié)點(diǎn)的信息 String tag=(String) tags.peek(); if (tag.equals(text) text=new String(p0,p1
55、,p2); else if (tag.equals(url) url=new String(p0,p1,p2); else if (tag.equals(author) author=new String(p0,p1,p2); else if (tag.equals(day) day=new String(p0,p1,p2); else if (tag.equals(month) month=new String(p0,p1,p2); else if (tag.equals(year) year=new String(p0,p1,p2); else if (tag.equals(descrip
56、tion) year=new String(p0,p1,p2); private void printout() System.out.print(Content: ); System.out.println(text); System.out.print(URL: ); System.out.println(url); System.out.print(Author: ); System.out.println(author); System.out.print(Date: ); System.out.println(day+-+month+-+year); System.out.print
57、(Description: ); System.out.println(description); System.out.println(); static public void main(String args) String filename = null; boolean validation = false; filename=links.xml; SAXParserFactory spf = SAXParserFactory.newInstance();SAXParser saxParser=null; try saxParser = spf.newSAXParser(); cat
58、ch (Exception ex) System.err.println(ex); System.exit(1); try saxParser.parse(new File(filename),new SAXReader(); catch (SAXException se) System.err.println(se.getMessage(); System.exit(1); catch (IOException ioe) System.err.println(ioe); System.exit(1); 在 這兒雖然沒有使用到棧的分析,但實(shí)際上棧的分析是一件很容易的事情,應(yīng)為java.util
59、.Stack繼承了java.util.Vector類,而且 Stack中的元素是按棧的結(jié)構(gòu)由底至上排列的,因個(gè),我們可以使用Vector類的size()方法來得到Stack的元素個(gè)數(shù),還可以使用 Vector的get(int)方法來得到具體的每一個(gè)元屬。實(shí)際上,如果把Stack的元素從底向上逐一排列出來,我們就得到了從XML根節(jié)點(diǎn)到當(dāng)前節(jié) 點(diǎn)的一條唯一的路徑,有了這條路徑的信息,文檔的結(jié)構(gòu)就在清楚不過了。 小節(jié) 好了,到這兒為止,我們已經(jīng)掌握了對(duì)于XML編 程的兩大利器:DOM和SAX,也知道了該如何在一個(gè)Java程序中使用它們。DOM編程相對(duì)簡(jiǎn)單,但是速度比較慢,占用內(nèi)存多,而SAX編程復(fù)雜
60、一些, 但是速度快,占用內(nèi)存少。所以,我們應(yīng)該根據(jù)不同的環(huán)境選擇使用不同的方法。大部分的XML應(yīng)用基本都可以用它們來解決。需要特別說明的是,DOM和 SAX其實(shí)都是語(yǔ)言無(wú)關(guān)的,并非Java所獨(dú)有,也就是說,只要有相應(yīng)的語(yǔ)言實(shí)現(xiàn),DOM和SAX可以應(yīng)用在任何面向?qū)ο蟮恼Z(yǔ)言中。 附錄資料:如何處理Java異常及常見異常六種異常處理的陋習(xí)你覺得自己是一個(gè)Java專家嗎?是否肯定自己已經(jīng)全面掌握了Java的異常處理機(jī)制?在下面這段代碼中,你能夠迅速找出異常處理的六個(gè)問題嗎? 1 OutputStreamWriter out = . 2 java.sql.Connection conn = . 3 tr
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度教育機(jī)構(gòu)打印機(jī)租賃及教學(xué)資源支持合同3篇
- 2025年度貨車承包運(yùn)營(yíng)管理與培訓(xùn)合同4篇
- 二零二五年度客運(yùn)熱氣球客運(yùn)服務(wù)合同模板4篇
- 二零二五版苗木種植基地環(huán)境治理合同7篇
- 2025年度酒店餐飲設(shè)施維護(hù)保養(yǎng)合同3篇
- 2025年度儲(chǔ)能設(shè)備箱涵設(shè)備調(diào)試與驗(yàn)收勞務(wù)分包合同4篇
- 2025年度插畫設(shè)計(jì)作品版權(quán)交易合同4篇
- 2025年度專業(yè)藝術(shù)團(tuán)體藝術(shù)指導(dǎo)教師長(zhǎng)期聘用合同
- 2025年人事代理合作合同
- 2025年度空調(diào)設(shè)備安全使用與維修合同范文
- 衡水市出租車駕駛員從業(yè)資格區(qū)域科目考試題庫(kù)(全真題庫(kù))
- 護(hù)理安全用氧培訓(xùn)課件
- 《三國(guó)演義》中人物性格探析研究性課題報(bào)告
- 注冊(cè)電氣工程師公共基礎(chǔ)高數(shù)輔導(dǎo)課件
- 土方勞務(wù)分包合同中鐵十一局
- 乳腺導(dǎo)管原位癌
- 冷庫(kù)管道應(yīng)急預(yù)案
- 司法考試必背大全(涵蓋所有法律考點(diǎn))
- 公共部分裝修工程 施工組織設(shè)計(jì)
- 《學(xué)習(xí)教育重要論述》考試復(fù)習(xí)題庫(kù)(共250余題)
- 裝飾裝修施工及擔(dān)保合同
評(píng)論
0/150
提交評(píng)論