這里我將向大家介紹處理XML文檔的另一個(gè)重要接口SAX(Simple?API?for?XML)。其中包括它的基本情況,它的API,一個(gè)開(kāi)發(fā)實(shí)例,實(shí)際開(kāi)發(fā)中一些需注意的問(wèn)題,以及它與DOM的對(duì)比。
SAX的基本情況
SAX同DOM一樣也是一個(gè)訪問(wèn)XML文檔的接口。SAX是Simple?API?for?XML的縮寫(xiě)。它不像DOM那樣是W3C的推薦標(biāo)準(zhǔn)。它是由XML-DEV郵件列表的成員開(kāi)發(fā)維護(hù),由David?Megginson領(lǐng)導(dǎo)(david@megginson.com)的一個(gè)Public?Domain軟件。SAX是一個(gè)徹底的自由軟件,它的作者放棄了對(duì)它的所有權(quán)利,并且它也被許可用于任何目的(在文章最后附錄了它的版權(quán)聲明)。
到現(xiàn)在為止SAX的版本已經(jīng)發(fā)展到2.0。在這個(gè)最新版本中增加了對(duì)名稱(chēng)空間(Namespaces)的支持,而且可以通過(guò)對(duì)features以及properties的設(shè)置來(lái)對(duì)解析器做全面的配置,這其中包括設(shè)置解析器是否對(duì)文檔進(jìn)行有效性驗(yàn)證,以及怎樣來(lái)處理帶有名稱(chēng)空間的元素名稱(chēng)等。SAX1中的接口已經(jīng)不再使用了,這里只會(huì)討論有關(guān)SAX2的開(kāi)發(fā)。在本文中提到SAX只是指SAX?2。另外,本文的所有例子都是用java編寫(xiě),SAX解析器也使用的是JAVA版本。
像DOM一樣,SAX并不是一個(gè)實(shí)際可以使用的XML文檔解析器,而是其他兼容SAX的解析器要實(shí)現(xiàn)的接口和幫助類(lèi)的集合。如果你想使用SAX的話,你必須滿足下面的要求:?
系統(tǒng)中包含Java?1.1?或者更高版本。?
在Java?classpath中包含進(jìn)你的SAX類(lèi)庫(kù)。?
在Java?classpath中包含進(jìn)你要使用的兼容SAX的XML解析器類(lèi)庫(kù)。
實(shí)現(xiàn)了SAX的解析器有很多,比如Apache的Xerces,Oracle的XML?Parser等等。在本文中的例子程序使用的都是Xerces解析器,你可以從?http://xml.apache.org?得到它。讓我們下載得到xerces.jar文件然后將其加入到classpath中去,這樣我們就已經(jīng)建立好環(huán)境(在xerces.jar中已經(jīng)包含了SAX接口,所以不必特意再去尋找SAX類(lèi)庫(kù))。
在SAX?API中有兩個(gè)包,org.xml.sax和org.xml.sax.helper。其中org.xml.sax中主要定義了SAX的一些基礎(chǔ)接口,如XMLReader、ContentHandler、ErrorHandler、DTDHandler、EntityResolver等。而在org.xml.sax.helper中則是一些方便開(kāi)發(fā)人員使用的幫助類(lèi),如缺省實(shí)現(xiàn)所有處理器接口的幫助類(lèi)DefaultHandler、方便開(kāi)發(fā)人員創(chuàng)建XMLReader的XMLReaderFactory類(lèi)等等。在這兩個(gè)包中還有一些應(yīng)用于SAX1的接口,同時(shí)還有幾個(gè)類(lèi)它們只是為了便于將在SAX1上開(kāi)發(fā)的應(yīng)用移植到SAX2上,在這篇文章中就不涉及了。下面是我們要關(guān)注的接口和類(lèi):
Package?org.xml.sax?介紹?
Interfaces?接口?
Attributes?定義了一個(gè)屬性列表接口,供訪問(wèn)元素的屬性列表而用。?
ContentHandler?處理解析文檔內(nèi)容時(shí)產(chǎn)生的事件。?
DTDHandler?處理解析DTD時(shí)的相應(yīng)事件。?
EntityResolver?處理外部實(shí)體。?
ErrorHandler?處理解析過(guò)程中所遇到的文檔錯(cuò)誤事件。?
Locator?為了定位解析中產(chǎn)生的內(nèi)容事件在文檔中的位置而準(zhǔn)備的一個(gè)定位器接口。?
XMLFilter?提供了一個(gè)方便應(yīng)用開(kāi)發(fā)的過(guò)濾器接口。?
XMLReader?任何兼容SAX2的解析器都要實(shí)現(xiàn)這個(gè)接口,這個(gè)接口讓?xiě)?yīng)用程序可以設(shè)置或查找features和properties,注冊(cè)各種事件處理器,以及開(kāi)始解析文檔。?
Classes?
InputSource?為XML實(shí)體準(zhǔn)備的輸入源。?
Exceptions?
SAXException?包裝了一般的SAX錯(cuò)誤和警告。??
SAXNotRecognizedException?為識(shí)別不出某些標(biāo)識(shí)而拋出的異常。??
SAXNotSupportedException?為不支持某個(gè)操作而拋出的異常。?
SAXParseException?包裝了一個(gè)關(guān)于XML解析的錯(cuò)誤或者警告。?
Package?org.xml.sax.helpers?幫助類(lèi)所在的包?
Classes?類(lèi)?
AttributesImpl?對(duì)Attributes接口的缺省實(shí)現(xiàn)?
NamespaceSupport?提供名稱(chēng)空間支持。?
DefaultHandler?缺省實(shí)現(xiàn)了四個(gè)處理器接口,方便用戶(hù)開(kāi)發(fā),在開(kāi)發(fā)過(guò)程中會(huì)經(jīng)常用到。?
LocatorImpl?提供了一個(gè)對(duì)Locator接口的實(shí)現(xiàn)?
XMLFilterImpl?對(duì)過(guò)濾器接口的實(shí)現(xiàn),使用過(guò)濾器進(jìn)行應(yīng)用程序開(kāi)發(fā)時(shí),繼承這個(gè)類(lèi)很方便。?
XMLReaderFactory?為方便創(chuàng)建不同的XMLReader而提供。也會(huì)經(jīng)常用到。?
理解并使用SAX
SAX的設(shè)計(jì)實(shí)現(xiàn)與DOM是完全不同的!DOM處理XML文檔是基于將XML文檔解析成樹(shù)狀模型,放入內(nèi)存進(jìn)行處理。而SAX則是采用基于事件驅(qū)動(dòng)的處理模式,它將XML文檔轉(zhuǎn)化成一系列的事件,由單獨(dú)的事件處理器來(lái)決定如何處理。為了了解如何使用SAX?API處理XML文檔,這里先介紹一下SAX所使用的基于事件驅(qū)動(dòng)的處理模式。
這種基于事件的處理模式是一種通用的程序設(shè)計(jì)模式,被廣泛應(yīng)用于GUI設(shè)計(jì)。在JAVA的AWT,SWING以及JAVA?BEANS中就有它的身影。而SAX的基于事件驅(qū)動(dòng)的處理模式就與上面三者中的非常相像。
基于事件的處理模式主要是圍繞著事件源以及事件處理器(或者叫監(jiān)聽(tīng)器)來(lái)工作的。一個(gè)可以產(chǎn)生事件的對(duì)象被稱(chēng)為事件源,而可以針對(duì)事件產(chǎn)生響應(yīng)的對(duì)象就被叫做事件處理器。事件源和事件處理器是通過(guò)在事件源中的事件處理器注冊(cè)方法連接的。這樣當(dāng)事件源產(chǎn)生事件后,調(diào)用事件處理器相應(yīng)的處理方法,一個(gè)事件就獲得了處理。當(dāng)然在事件源調(diào)用事件處理器中特定方法的時(shí)候,會(huì)傳遞給事件處理器相應(yīng)事件的狀態(tài)信息,這樣事件處理器才能夠根據(jù)事件信息來(lái)決定自己的行為。
在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通過(guò)parse()方法來(lái)開(kāi)始解析XML文檔并根據(jù)文檔內(nèi)容產(chǎn)生事件。而事件處理器則是org.xml.sax包中的ContentHandler,DTDHandler,ErrorHandler,以及EntityResolver這四個(gè)接口。它們分別處理事件源在解析過(guò)程中產(chǎn)生的不同種類(lèi)的事件(其中DTDHandler是為解析文檔DTD時(shí)而用)。而事件源XMLReader和這四個(gè)事件處理器的連接是通過(guò)在XMLReader中的相應(yīng)的事件處理器注冊(cè)方法set***()來(lái)完成的。詳細(xì)介紹請(qǐng)見(jiàn)下表:?處理器名稱(chēng)?所處理事件?注冊(cè)方法?
org.xml.sax.ContentHandler?跟文檔內(nèi)容有關(guān)的所有事件:?
文檔的開(kāi)始和結(jié)束?
XML元素的開(kāi)始和結(jié)束?
可忽略的實(shí)體?
名稱(chēng)空間前綴映射開(kāi)始和結(jié)束?
處理指令?
字符數(shù)據(jù)和可忽略的空格?
?XMLReader中的setContentHandler(ContentHandler?handler)方法?
org.xml.sax.ErrorHandler?處理XML文檔解析時(shí)產(chǎn)生的錯(cuò)誤。如果一個(gè)應(yīng)用程序沒(méi)有注冊(cè)一個(gè)錯(cuò)誤處理器類(lèi),會(huì)發(fā)生不可預(yù)料的解析器行為。?setErrorHandler(ErrorHandler?handler)?
org.xml.sax.DTDHandler?處理對(duì)文檔DTD進(jìn)行解析時(shí)產(chǎn)生的相應(yīng)事件?setDTDHandler(DTDHandler?handler)?
org.xml.sax.EntityResolver?處理外部實(shí)體?setEntityResolver(EntityResolver?resolver)?
在這四個(gè)處理器接口中,對(duì)我們最重要的是ContentHandler接口。下面讓我們看一下對(duì)其中方法的說(shuō)明:?方法名稱(chēng)?方法說(shuō)明?
public?void?setDocumentLocator(Locator?locator)?設(shè)置一個(gè)可以定位文檔內(nèi)容事件發(fā)生位置的定位器對(duì)象?
public?void?startDocument()?throws?SAXException?用于處理文檔解析開(kāi)始事件?
public?void?endDocument()?throws?SAXException?用于處理文檔解析結(jié)束事件?
public?void?startPrefixMapping(java.lang.String?prefix,?java.lang.String?uri)?throws?SAXException?用于處理前綴映射開(kāi)始事件,從參數(shù)中可以得到前綴名稱(chēng)以及所指向的uri?
public?void?endPrefixMapping(java.lang.String?prefix)?throws?SAXException?用于處理前綴映射結(jié)束事件,從參數(shù)中可以得到前綴名稱(chēng)?
public?void?startElement(java.lang.String?namespaceURI,java.lang.String?localName,java.lang.String?qName,Attributes?atts)?throws?SAXException?處理元素開(kāi)始事件,從參數(shù)中可以獲得元素所在名稱(chēng)空間的uri,元素名稱(chēng),屬性列表等信息?
public?void?endElement(java.lang.String?namespaceURI,?java.lang.String?localName,?java.lang.String?qName)?throws?SAXException?處理元素結(jié)束事件,從參數(shù)中可以獲得元素所在名稱(chēng)空間的uri,元素名稱(chēng)等信息?
public?void?characters(char[]?ch,?int?start,?int?length)?throws?SAXException?處理元素的字符內(nèi)容,從參數(shù)中可以獲得內(nèi)容?
public?void?ignorableWhitespace(char[]?ch,?int?start,?int?length)?throws?SAXException?處理元素的可忽略空格?
public?void?processingInstruction(java.lang.String?target,?java.lang.String?data)?throws?SAXException?處理解析中產(chǎn)生的處理指令事件?
這里再介紹一下org.xml.sax.XMLReader中的方法,然后讓我們看一個(gè)具體的例子。XMLReader是所有兼容SAX2的解析器都要實(shí)現(xiàn)的接口,由它的方法開(kāi)始解析文檔,并且調(diào)用它的注冊(cè)方法來(lái)注冊(cè)各種事件處理器。請(qǐng)看下表:?方法名稱(chēng)?方法介紹?
public?Boolean?getFeature(java.lang.String?name)throws?SAXNotRecognizedException,SAXNotSupportedException?得到某個(gè)feature的值?
public?void?setFeature(java.lang.String?name,boolean?value)?throws?SAXNotRecognizedException,SAXNotSupportedException?設(shè)置某個(gè)feature的值,例如,如果需要解析器支持對(duì)文檔進(jìn)行驗(yàn)證那么就這么調(diào)用本方法。myReader.setFeature(http://xml.org/sax/features/validation,true);其中myReader是XMLReader的實(shí)例。?
public?java.lang.Object?getProperty(java.lang.String?name)throws?SAXNotRecognizedException,SAXNotSupportedException?返回一個(gè)property的值?
public?void?setProperty(java.lang.String?name,java.lang.Object?value)throws?SAXNotRecognizedException,SAXNotSupportedException?設(shè)置一個(gè)property的值?
public?void?setEntityResolver(EntityResolver?resolver)?注冊(cè)處理外部實(shí)體的EntityResolver?
public?EntityResolver?getEntityResolver()?得到系統(tǒng)中注冊(cè)的EntityResolver?
public?void?setDTDHandler(DTDHandler?handler)?注冊(cè)處理DTD解析事件的DTDHandler?
public?DTDHandler?getDTDHandler()?得到系統(tǒng)中注冊(cè)的DTDHandler?
public?void?setContentHandler(ContentHandler?handler)?注冊(cè)處理XML文檔內(nèi)容解析事件的ContentHandler?
public?ContentHandler?getContentHandler()?得到系統(tǒng)中注冊(cè)的ContentHandler?
public?void?setErrorHandler(ErrorHandler?handler)?注冊(cè)處理文檔解析錯(cuò)誤事件的ErrorHandler?
public?ErrorHandler?getErrorHandler()?得到系統(tǒng)中注冊(cè)的ErrorHandler?
public?void?parse(InputSource?input)throws?java.io.IOException,SAXException?開(kāi)始解析一個(gè)XML文檔。?
public?void?parse(java.lang.String?systemId)throws?java.io.IOException,SAXException?開(kāi)始解析一個(gè)使用系統(tǒng)標(biāo)識(shí)符標(biāo)識(shí)的XML文檔。這個(gè)方法只是上面方法的一個(gè)快捷方式它等同于:parse(new?InputSource(systemId));?
一個(gè)實(shí)例
讓我們通過(guò)例子來(lái)看一下使用SAX解析XML文檔的應(yīng)用程序是如何建立的。下面是在應(yīng)用程序中被處理的XML文檔。為了說(shuō)明SAX對(duì)名稱(chēng)空間的支持,我在這里特意加了一個(gè)有名稱(chēng)空間的元素,在這里會(huì)產(chǎn)生相應(yīng)的前綴映射開(kāi)始和結(jié)束事件。
<?xml?version="1.0"?encoding="GB2312"?>
<我的書(shū)架?>
????<技術(shù)書(shū)籍>
????????<圖書(shū)>
????????????<書(shū)名>JAVA?2編程詳解</書(shū)名>
????????????<價(jià)格?貨幣單位="人民幣">150</價(jià)格>
????????????<購(gòu)買(mǎi)日期>2000,1,24</購(gòu)買(mǎi)日期>
????????</圖書(shū)>??????
????</技術(shù)書(shū)籍>
????<book:文學(xué)書(shū)籍?xmlns:book="http://javausr.com"/>
????<歷史書(shū)籍/>
</我的書(shū)架>
?
這里的例子程序只是簡(jiǎn)單地將遇到的事件信息打印出來(lái)。我們首先實(shí)現(xiàn)ContentHandler接口來(lái)處理在XML文檔解析過(guò)程中產(chǎn)生的和文檔內(nèi)容相關(guān)的事件,代碼如下所示MyContentHandler.java:
package?com.javausr.saxexample;?
import?org.xml.sax.Attributes;
import?org.xml.sax.ContentHandler;
import?org.xml.sax.Locator;
import?org.xml.sax.SAXException;
public?class?MyContentHandler?implements?ContentHandler?{
????private?StringBuffer?buf;
????public?void?setDocumentLocator(?Locator?locator?)?{
????}
????public?void?startDocument()?throws?SAXException?{
????????buf=new?StringBuffer();
????????System.out.println("*******開(kāi)始解析文檔*******");
????}
????public?void?endDocument()?throws?SAXException?{
????????System.out.println("*******解析文檔結(jié)束*******");
????}
????public?void?processingInstruction(?String?target,?String?instruction?)
????????throws?SAXException?{
????}
????public?void?startPrefixMapping(?String?prefix,?String?uri?)?{
??????????System.out.println("\n前綴映射:?"?+?prefix?+"?開(kāi)始!"+?"??它的URI是:"?+?uri);
????}
????public?void?endPrefixMapping(?String?prefix?)?{
??????????System.out.println("\n前綴映射:?"+prefix+"?結(jié)束!");
????}
????public?void?startElement(?String?namespaceURI,?String?localName,
??????????????????????????????????String?fullName,?Attributes?attributes?)
??????????????????????????throws?SAXException?{
????????System.out.println("\n?元素:?"?+?"["+fullName+"]"?+"?開(kāi)始解析!");
????????//?打印出屬性信息
????????for?(?int?i?=?0;?i?<?attributes.getLength();?i++?)?{
????????????System.out.println("\t屬性名稱(chēng):"?+?attributes.getLocalName(i)
????????????????+?"?屬性值:"?+?attributes.getvalue(i));
????????}
????}
????public?void?endElement(?String?namespaceURI,?String?localName,
??????????????????????????????????????????????????????String?fullName?)
??????????????????????????throws?SAXException?{
????????//打印出非空的元素內(nèi)容并將StringBuffer清空??????????????????
??????String?nullStr="";
????????if?(!buf.toString().trim().equals(nullStr)){
???????????System.out.println("\t內(nèi)容是:?"?+?buf.toString().trim());
????????}
????????buf.setLength(0);
????????//打印元素解析結(jié)束信息
????????System.out.println("元素:?"+"["+fullName+"]"+"?解析結(jié)束!");??????????????
????}
????public?void?characters(?char[]?chars,?int?start,?int?length?)
????????????????????????????????throws?SAXException?{
??????????//將元素內(nèi)容累加到StringBuffer中????????????????
??????????buf.append(chars,start,length);
????}
????public?void?ignorableWhitespace(?char[]?chars,?int?start,?int?length?)
??????????????????????????????????throws?SAXException?{
????}
????public?void?skippedEntity(?String?name?)?throws?SAXException?{
????}
}
?
下面讓我們創(chuàng)建一個(gè)調(diào)入了xerces解析器來(lái)實(shí)現(xiàn)XMLReader接口、并使用剛才創(chuàng)建的MyContentHandler來(lái)處理相應(yīng)解析事件的MySAXApp.java類(lèi):
package?com.javausr.saxexample;?
import?org.xml.sax.XMLReader;
import?org.xml.sax.helpers.XMLReaderFactory;
import?org.xml.sax.ContentHandler;
import?org.xml.sax.SAXException;
import?java.io.IOException;
public?class?MySAXApp?{
??public?static?void?main(?String[]?args?)?{
????
????if?(?args.length?!=?1?)?{
??????System.out.println("輸入:?java?MySAXApp?");
??????System.exit(0);
????}
????try?{
????????//?初始化reader
????????XMLReader?reader?=?XMLReaderFactory.createXMLReader
??????????????????????????("org.apache.xerces.parsers.SAXParser")?;
????????//?創(chuàng)建ContentHandler的實(shí)例
????????ContentHandler?contentHandler?=?new?MyContentHandler();
????????//?在reader中注冊(cè)實(shí)例化的ContentHandler
????????reader.setContentHandler(?contentHandler?);
????????//?開(kāi)始解析文檔
????????reader.parse(args[0]);
????}?catch?(?IOException?e?)?{
????????System.out.println("讀入文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}?catch?(?SAXException?e?)?{
????????System.out.println("解析文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}
??}
}
?
下面讓我們來(lái)看一下執(zhí)行結(jié)果:?
D:\sax\classes>java?com.javausr.saxexample.MySAXApp?d:\book.xml
*******開(kāi)始解析文檔*******
元素:?[我的書(shū)架]?開(kāi)始解析!
元素:?[技術(shù)書(shū)籍]?開(kāi)始解析!
元素:?[圖書(shū)]?開(kāi)始解析!
元素:?[書(shū)名]?開(kāi)始解析!
????????內(nèi)容是:?JAVA?2編程詳解
元素:?[書(shū)名]?解析結(jié)束!
元素:?[價(jià)格]?開(kāi)始解析!
????????屬性名稱(chēng):貨幣單位?屬性值:人民幣
????????內(nèi)容是:?150
元素:?[價(jià)格]?解析結(jié)束!
元素:?[購(gòu)買(mǎi)日期]?開(kāi)始解析!
????????內(nèi)容是:?2000,1,24
元素:?[購(gòu)買(mǎi)日期]?解析結(jié)束!
元素:?[圖書(shū)]?解析結(jié)束!
元素:?[技術(shù)書(shū)籍]?解析結(jié)束!
前綴映射:?book?開(kāi)始!??它的URI是:http://javausr.com
元素:?[book:文學(xué)書(shū)籍]?開(kāi)始解析!
元素:?[book:文學(xué)書(shū)籍]?解析結(jié)束!
前綴映射:?book?結(jié)束!
元素:?[歷史書(shū)籍]?開(kāi)始解析!
元素:?[歷史書(shū)籍]?解析結(jié)束!
元素:?[我的書(shū)架]?解析結(jié)束!
*******解析文檔結(jié)束*******
?
上面就是使用SAX解析一個(gè)XML文檔的基本過(guò)程,但是MyContentHandler只是處理了解析過(guò)程中和文檔內(nèi)容相關(guān)的事件,如果在解析過(guò)程中出現(xiàn)了錯(cuò)誤那我們需要實(shí)現(xiàn)ErrorHandler接口來(lái)處理。如果不注冊(cè)一個(gè)錯(cuò)誤處理器來(lái)處理的話,那么錯(cuò)誤事件將不會(huì)被報(bào)告,而且解析器會(huì)出現(xiàn)不可預(yù)知的行為。在解析過(guò)程中產(chǎn)生的錯(cuò)誤被分成了3類(lèi),它們分別是warning,error,以及fatalerror,也就是說(shuō)在ErrorHandler中有這么三個(gè)相應(yīng)的方法來(lái)處理這些錯(cuò)誤事件。下面是對(duì)這三個(gè)錯(cuò)誤處理方法的介紹:?方法名稱(chēng)?方法介紹?
warning()?SAX解析器將用這個(gè)方法來(lái)報(bào)告在XML1.0規(guī)范中定義的非錯(cuò)誤(error)或者致命錯(cuò)誤(fatal?error)的錯(cuò)誤狀態(tài)。對(duì)這個(gè)錯(cuò)誤缺省的行為是什么也不做。SAX解析器必須在調(diào)用這個(gè)方法后繼續(xù)提供正常的解析事件:應(yīng)用程序應(yīng)該能繼續(xù)處理完文檔。?
error()?這個(gè)方法對(duì)應(yīng)在W3C?XML?1.0規(guī)范的1.2部分中定義的"error"概念。例如,一個(gè)帶有有效性驗(yàn)證的解析器會(huì)使用這個(gè)方法來(lái)報(bào)告違反有效性驗(yàn)證的情況。一個(gè)帶有有效性驗(yàn)證的解析器會(huì)使用這個(gè)方法來(lái)報(bào)告違背有些性約束的情況。缺省的行為是什么也不做。SAX解析器必須在調(diào)用這個(gè)方法后繼續(xù)提供正常的解析事件:應(yīng)用程序應(yīng)該能繼續(xù)處理完文檔。如果應(yīng)用程序做不到這樣,則解析器即使在XML1.0規(guī)范沒(méi)有要求的情況下也要報(bào)告一個(gè)致命錯(cuò)誤。?
fatalError()?這個(gè)方法對(duì)應(yīng)在W3C?XML1.0規(guī)范的1.2部分定義的"fatal?error"概念。例如,一個(gè)解析器會(huì)使用這個(gè)方法來(lái)報(bào)告違反格式良好約束的情況。在解析器調(diào)用這個(gè)方法后應(yīng)用程序必須表明這個(gè)文檔是不可使用的,而且應(yīng)該只是為了收集錯(cuò)誤信息而繼續(xù)進(jìn)行處理(如果需要的話):實(shí)際上,一旦在這個(gè)方法被調(diào)用后SAX解析器可以停止報(bào)告任何事件。?
下面是實(shí)現(xiàn)了ErrorHandler接口的MyErrorHandler.java類(lèi):
package?com.javausr.saxexample;?
import?org.xml.sax.ErrorHandler;
import?org.xml.sax.SAXParseException;
import?org.xml.sax.SAXException;
public?class?MyErrorHandler?implements?ErrorHandler?{
????public?void?warning(?SAXParseException?exception?)?{
????????System.out.println("*******WARNING******");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("********************");
????}
????public?void?error(?SAXParseException?exception?)?throws?SAXException{
????????System.out.println("*******?ERROR?******");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("********************");
????}
????public?void?fatalError(?SAXParseException?exception?)?throws?SAXException?{
????????System.out.println("********?FATAL?ERROR?********");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("*****************************");
????}
}
?
我們也要對(duì)MySAXApp.java類(lèi)做一些修改(在源代碼中藍(lán)色標(biāo)出的部分)使它使用MyErrorHandler.java:
package?com.javausr.saxexample;?
import?org.xml.sax.XMLReader;
import?org.xml.sax.helpers.XMLReaderFactory;
import?org.xml.sax.ContentHandler;
//引入ErrorHandler
import?org.xml.sax.ErrorHandler;
import?org.xml.sax.SAXException;
import?java.io.IOException;
public?class?MySAXApp?{
????
????????public?static?void?main(?String[]?args?)?{
????????
??????????if?(?args.length?!=?1?)?{
????????????System.out.println("輸入:?java?MySAXApp?");
????????????System.exit(0);
????????}
????????try?{
????????????//?初始化reader
????????????XMLReader?reader?=?XMLReaderFactory.createXMLReader
???????????????????????????????("org.apache.xerces.parsers.SAXParser")?;
????????????//?創(chuàng)建ContentHandler的實(shí)例
????????????ContentHandler?contentHandler?=?new?MyContentHandler();
????????????//?在reader中注冊(cè)實(shí)例化的ContentHandler
????????????reader.setContentHandler(?contentHandler?);
????????????//?創(chuàng)建ErrorHandler的實(shí)例
????????????ErrorHandler?errorHandler?=?new?MyErrorHandler();
????????????//?在reader中注冊(cè)實(shí)例化的ErrorHandler
????????????reader.setErrorHandler(?errorHandler?);
????????????//?開(kāi)始解析文檔
????????????reader.parse(args[0]);
????}?catch?(?IOException?e?)?{
????????System.out.println("讀入文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}?catch?(?SAXException?e?)?{
????????System.out.println("解析文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}
??}
?
讓我們?nèi)藶橹圃煨╁e(cuò)誤來(lái)檢查一下我們的錯(cuò)誤處理器工作情況。刪除元素<購(gòu)買(mǎi)日期>的閉合標(biāo)記,這樣會(huì)產(chǎn)生一個(gè)fatal?error,下面是執(zhí)行結(jié)果:
D:\sax\classes>java?com.javausr.saxexample.MySAXApp?d:\book.xml?
*******開(kāi)始解析文檔*******
元素:?[我的書(shū)架]?開(kāi)始解析!
元素:?[技術(shù)書(shū)籍]?開(kāi)始解析!
元素:?[圖書(shū)]?開(kāi)始解析!
元素:?[書(shū)名]?開(kāi)始解析!
元素:?[書(shū)名]?開(kāi)始解析!
????????內(nèi)容是:?JAVA?2編程詳解
元素:?[書(shū)名]?解析結(jié)束!
元素:?[價(jià)格]?開(kāi)始解析!
????????屬性名稱(chēng):貨幣單位?屬性值:人民幣
????????內(nèi)容是:?150
元素:?[價(jià)格]?解析結(jié)束!
元素:?[購(gòu)買(mǎi)日期]?開(kāi)始解析!
********?FATAL?ERROR?********
????????行:?????8
????????列:?????7
????????錯(cuò)誤信息:???????The?element?type?"購(gòu)買(mǎi)日期"?must?be?terminated?by?the?matching?end-tag?"</購(gòu)買(mǎi)日期>".
*****************************
解析文檔時(shí)錯(cuò):?Stopping?after?fatal?error:?The?element?type?"購(gòu)買(mǎi)日期"?must?be?terminated?by?the?matching?end-tag?"</購(gòu)買(mǎi)日期>".
?
現(xiàn)在總結(jié)一下如何書(shū)寫(xiě)基于SAX的應(yīng)用程序。一般步驟如下:?
實(shí)現(xiàn)一個(gè)或多個(gè)處理器接口(ContentHandler,?ErrorHandler,?DTDHandler?,or?EntityResover)。?
創(chuàng)建一個(gè)XMLReader類(lèi)的實(shí)例。?
在新的XMLReader實(shí)例中通過(guò)大量的set*****()?方法注冊(cè)一個(gè)事件處理器的實(shí)例?
調(diào)用XMLReader的parse()方法來(lái)處理文檔。
使用DefaultHandler
現(xiàn)在的程序是比較完整了,但還有許多可以改進(jìn)的地方。首先在我們實(shí)現(xiàn)的MyContentHandler.java中,你會(huì)發(fā)現(xiàn)有很多方法實(shí)際上什么也沒(méi)有做,但為了實(shí)現(xiàn)ContentHandler接口,不得不把它們寫(xiě)出來(lái),這樣很是麻煩。SAX?API已經(jīng)考慮到這個(gè)問(wèn)題,在它的org.xml.sax.helper包中為我們提供了一個(gè)方便實(shí)現(xiàn)各種處理器接口的幫助類(lèi)DefaultHandler。這個(gè)類(lèi)缺省實(shí)現(xiàn)了上面提到的4個(gè)處理器接口。這樣我們只需繼承這個(gè)類(lèi),然后覆蓋我們想要實(shí)現(xiàn)的事件處理方法即可。下面我們來(lái)新建一個(gè)繼承了DefaultHandler的MyDefaultHandler.java類(lèi),然后把在MyContentHandler.java和MyErrorHandler.java中實(shí)現(xiàn)的事件處理方法照搬到MyDefaultHandler.java類(lèi)中,那些沒(méi)有使用的方法就不必重復(fù)了。這里是MyDefaultHandler.java:
package?com.javausr.saxexample;?
import?org.xml.sax.*;
import?org.xml.sax.helpers.*;
import?java.io.*;
public?class?MyDefaultHandler?extends?DefaultHandler?{
????private?StringBuffer?buf;
????public?void?startDocument()?throws?SAXException?{
????????buf=new?StringBuffer();
????????System.out.println("*******開(kāi)始解析文檔*******");
????}
????public?void?endDocument()?throws?SAXException?{
????????System.out.println("*******解析文檔結(jié)束*******");
????}
????public?void?startPrefixMapping(?String?prefix,?String?uri?)?{
System.out.println("\n前綴映射:?"?+?prefix?+"?開(kāi)始!"+?"??它的URI是:"+uri);
????}
????public?void?endPrefixMapping(?String?prefix?)?{
???????System.out.println("\n前綴映射:?"+prefix+"?結(jié)束!");
????}
????public?void?startElement(?String?namespaceURI,?String?localName,
??????????????????????????????????String?fullName,?Attributes?attributes?)
??????????????????????????throws?SAXException?{
????????System.out.println("\n元素:?"?+?"["+fullName+"]"?+"?開(kāi)始解析!");
????????//?打印出屬性信息
????????for?(?int?i?=?0;?i?<?attributes.getLength();?i++?)?{
????????????System.out.println("\t屬性名稱(chēng):"?+?attributes.getLocalName(i)
????????????????+?"?屬性值:"?+?attributes.getvalue(i));
????????}
????}
????public?void?endElement(?String?namespaceURI,?String?localName,
??????????????????????????????????????????????????????String?fullName?)
??????????????????????????throws?SAXException?{
???????//打印出非空的元素內(nèi)容并將StringBuffer清空
???????String?nullStr="";
???????if?(!buf.toString().trim().equals(nullStr)){
??????????System.out.println("\t內(nèi)容是:?"?+?buf.toString().trim());
???????}
???????buf.setLength(0);
???????//打印元素解析結(jié)束信息
????????System.out.println("元素:?"+"["+fullName+"]"+"?解析結(jié)束!");
????}
????public?void?characters(?char[]?chars,?int?start,?int?length?)
????????????????????????????????throws?SAXException?{
???????//將元素內(nèi)容累加到StringBuffer中
???????buf.append(chars,start,length);
????}
????public?void?warning(?SAXParseException?exception?)?{
????????System.out.println("*******WARNING******");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("********************");
????}
????public?void?error(?SAXParseException?exception?)?throws?SAXException{
????????System.out.println("*******?ERROR?******");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("********************");
????}
????public?void?fatalError(?SAXParseException?exception?)?throws?SAXException?{
????????System.out.println("********?FATAL?ERROR?********");
????????System.out.println("\t行:\t"?+?exception.getLineNumber());
????????System.out.println("\t列:\t"?+?exception.getColumnNumber());
????????System.out.println("\t錯(cuò)誤信息:\t"?+?exception.getMessage());
????????System.out.println("*****************************");
????}
}
?
我們也要對(duì)MySAXApp.java做相應(yīng)的修改,修改已在源代碼中標(biāo)出:
package?com.javausr.saxexample;?
import?org.xml.sax.XMLReader;
import?org.xml.sax.helpers.XMLReaderFactory;
//引入DefaultHandler
import?org.xml.sax.helpers.DefaultHandler;
import?org.xml.sax.SAXException;
import?java.io.IOException;
public?class?MySAXApp?{
??public?static?void?main(?String[]?args?)?{
????????
??????if?(?args.length?!=?1?)?{
????????System.out.println("輸入:?java?MySAXApp?");
????????System.exit(0);
??????}
????try?{
????????//?初始化reader
????????XMLReader?reader?=?XMLReaderFactory.createXMLReader
?????????????????????????("org.apache.xerces.parsers.SAXParser")?;
????????//?創(chuàng)建DefaultHandler的實(shí)例
????????DefaultHandler?defaultHandler=new?MyDefaultHandler();
????????//在reader中將defaultHandler注冊(cè)為ContentHandler
????????reader.setContentHandler(defaultHandler);
????????//在reader中將defaultHandler注冊(cè)為ErrorHandler
????????reader.setErrorHandler(defaultHandler);
????????//?開(kāi)始解析文檔
????????reader.parse(args[0]);
????}?catch?(?IOException?e?)?{
????????System.out.println("讀入文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}?catch?(?SAXException?e?)?{
????????System.out.println("解析文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}
??}
}
?
使用過(guò)濾器
在SAX?API中還提供了一個(gè)過(guò)濾器接口org.xml.sax.XMLFilter,以及對(duì)它的缺省實(shí)現(xiàn)org.xml.sax.helper.XMLFilterImpl。使用它們可以很容易的開(kāi)發(fā)出復(fù)雜的SAX應(yīng)用。這里要先介紹一下過(guò)濾器設(shè)計(jì)模式。這個(gè)設(shè)計(jì)模式很好理解,就像一個(gè)凈化水的過(guò)程。自然界中的水流過(guò)一個(gè)個(gè)的過(guò)濾器得到最后的飲用水。這些過(guò)濾器,有的是清除水中的泥沙,有的是殺滅水中的細(xì)菌,總之不同的過(guò)濾器完成不同的任務(wù)。在應(yīng)用開(kāi)發(fā)中,我們讓被改造的對(duì)象(這里是事件流)通過(guò)這些過(guò)濾器對(duì)象從而得到改造后符合要求的對(duì)象。這樣,在過(guò)濾器的幫助之下,我們可以非常方便的在每個(gè)過(guò)濾器中實(shí)現(xiàn)一個(gè)特定功能,從而創(chuàng)建結(jié)構(gòu)復(fù)雜的應(yīng)用程序。在應(yīng)用程序中你可以構(gòu)造任意多個(gè)過(guò)濾器,將它們串接起來(lái)完成任務(wù)。
在SAX?API中org.xml.sax.XMLFilter接口繼承了org.xml.sax.XMLReader接口。它與XMLReader不同的是它不像XMLReader那樣通過(guò)解析文檔來(lái)獲取事件,而是從其他XMLReader中獲取事件,當(dāng)然這也包括從其他的XMLFilter中獲取事件。在org.xml.sax.XMLFilter中有兩個(gè)方法:?方法名稱(chēng)?方法描述?
Public?void?setParent(XMLReader?parent)?設(shè)置父XMLReader。這個(gè)方法讓?xiě)?yīng)用程序?qū)⑦@個(gè)過(guò)濾器連接到它的父XMLReader?(也可能是另一個(gè)過(guò)濾器)。??
Public?XMLReader?getParent()?獲取父XMLReader。這個(gè)方法讓?xiě)?yīng)用程序可以查詢(xún)父XMLReader(也可能是另一個(gè)過(guò)濾器)。最好不要在父XMLReader中直接進(jìn)行任何操作:讓所有的事件通過(guò)這個(gè)過(guò)濾器來(lái)處理。?
我們不需要自己實(shí)現(xiàn)org.xml.sax.XMLFilter接口,在SAX?API?中提供了一個(gè)org.xml.sax.helper.XMLFilterImpl類(lèi),它不僅實(shí)現(xiàn)了org.xml.sax.XMLFilter接口而且還實(shí)現(xiàn)了其他四個(gè)核心處理器接口,我們只需要繼承它即可完成我們的過(guò)濾器。剛開(kāi)始使用XMLFilterImpl比較容易讓人迷惑,你只需要記住:?
在你繼承的XMLFilterImpl類(lèi)中用set****()方法這冊(cè)的事件處理器是給過(guò)濾后的事件流而用的。?
在你繼承的XMLFilterImpl類(lèi)中實(shí)現(xiàn)的那些事件處理方法,比如startDocument()、startElement()、characters()等才是這個(gè)過(guò)濾器實(shí)現(xiàn)它自身功能的地方。而通過(guò)繼承XMLFilterImpl而實(shí)現(xiàn)的這個(gè)類(lèi)會(huì)被造型成各種處理器(它本身實(shí)現(xiàn)了四個(gè)處理器接口)用在它的父XMLReader中。這個(gè)步驟會(huì)在你調(diào)用自己創(chuàng)建的過(guò)濾器的parse()方法開(kāi)始解析文檔時(shí)被自動(dòng)執(zhí)行(請(qǐng)參見(jiàn)SAX源代碼)。?
如果不是使用帶參數(shù)的構(gòu)造器創(chuàng)建XMLFilter對(duì)象,務(wù)必使用setParent(XMLReader?parent)方法連接它的父XMLReader。?
如果使用多個(gè)過(guò)濾器的話,執(zhí)行順序是從父親到最后的過(guò)濾器。但是開(kāi)始解析卻要調(diào)用最后一個(gè)過(guò)濾器的parse()方法。
下面讓我們結(jié)合已有的例子來(lái)演示過(guò)濾器org.xml.sax.XMLFilter的作用。我們?cè)谶@個(gè)過(guò)濾器中要過(guò)濾掉<技術(shù)書(shū)籍>這個(gè)元素,最后得到的事件流還是由上邊實(shí)現(xiàn)的MyDefaultHandler來(lái)處理。源代碼如下MyFilter.java:
package?com.javausr.saxexample;?
import?org.xml.sax.*;
import?org.xml.sax.helpers.*;
import?java.io.*;
public?class?MyFilter?extends?XMLFilterImpl?{
???private?String?currentElement;
???public?MyFilter(?XMLReader?parent?)?{
??????super(parent);
???}
???/**
????*?過(guò)濾掉元素<技術(shù)書(shū)籍>的開(kāi)始事件
????**/
???public?void?startElement(?String?namespaceURI,?String?localName,
?????????????????????????????String?fullName,?Attributes?attributes?)
??????throws?SAXException?{
?????????currentElement?=?localName;
?????????if?(?!localName.equals("技術(shù)書(shū)籍")?)?{
???????????super.startElement(namespaceURI,?localName,?fullName,?attributes);
?????????}
??????}
???/**
????*?過(guò)濾掉元素<技術(shù)書(shū)籍>的結(jié)束事件
????**/
???public?void?endElement(String?namespaceURI,?String?localName,?String
??????????????????????????fullName)
??????throws?SAXException?{
?????????if?(?!localName.equals("技術(shù)書(shū)籍")?)?{
????????????super.endElement(namespaceURI,?localName,?fullName);
?????????}
????}
???/**
????*?過(guò)濾掉元素<技術(shù)書(shū)籍>中的內(nèi)容
????**/
????public?void?characters(char[]?buffer,?int?start,?int?length)?
throws?SAXException?{
????????if?(?!currentElement.equals("技術(shù)書(shū)籍")?)?{
??????????super.characters(?buffer,start,length?);
????????}
????}
}
?
同樣我們還要修改MySAXApp.java,修改后的代碼如下所示:
package?com.javausr.saxexample;?
import?org.xml.sax.XMLReader;
import?org.xml.sax.helpers.XMLReaderFactory;
import?org.xml.sax.helpers.DefaultHandler;
//引入XMLFilter
import?org.xml.sax.XMLFilter;
import?org.xml.sax.SAXException;
import?java.io.IOException;
public?class?MySAXApp?{
??public?static?void?main(?String[]?args?)?{
????
????if?(?args.length?!=?1?)?{
??????System.out.println("輸入:?java?MySAXApp?");
??????System.exit(0);
????}
????try?{
????????????//?初始化reader
????????XMLReader?reader?=?XMLReaderFactory.createXMLReader
???????????????????????????("org.apache.xerces.parsers.SAXParser")?;
????????//初始化過(guò)濾器
????????XMLFilter?myFilter=new?MyFilter(reader);
????????//?創(chuàng)建DefaultHandler的實(shí)例
????????DefaultHandler?defaultHandler=new?MyDefaultHandler();
????????//為過(guò)濾后的事件流設(shè)置ContentHandler
????????myFilter.setContentHandler(defaultHandler);
????????//為過(guò)濾后的事件流設(shè)置ErrorHandler
????????myFilter.setErrorHandler(defaultHandler);
????????????//?開(kāi)始解析文檔,注意是使用myFilter中的解析方法
????????myFilter.parse(args[0]);
??????}?catch?(?IOException?e?)?{
????????????System.out.println("讀入文檔時(shí)錯(cuò):?"?+?e.getMessage());
??????}?catch?(?SAXException?e?)?{
????????????System.out.println("解析文檔時(shí)錯(cuò):?"?+?e.getMessage());
????}
??}
}
?
這里是最后的執(zhí)行結(jié)果,我們可以發(fā)現(xiàn)有關(guān)<技術(shù)書(shū)籍>的全部事件已經(jīng)被過(guò)濾掉了。認(rèn)真看一下結(jié)果,你一定覺(jué)得奇怪,為什么<技術(shù)書(shū)籍>元素的孩子元素仍然存在。請(qǐng)記住SAX是把XML文檔解析成事件流,所有沒(méi)有被過(guò)濾的事件都會(huì)保留下來(lái)。這就是SAX和DOM的最大不同。在DOM中文檔被解析成了樹(shù)狀模型,如果你刪除一個(gè)元素,那么這個(gè)元素以及它的孩子元素就都會(huì)被刪除,這符合樹(shù)狀模型的特點(diǎn)。
D:\sax\classes>java?com.javausr.saxexample.MySAXApp?d:\book.xml?
*******開(kāi)始解析文檔*******
元素:?[我的書(shū)架]?開(kāi)始解析!
元素:?[圖書(shū)]?開(kāi)始解析!
元素:?[書(shū)名]?開(kāi)始解析!
????????內(nèi)容是:?JAVA?2編程詳解
元素:?[書(shū)名]?解析結(jié)束!
元素:?[價(jià)格]?開(kāi)始解析!
????????屬性名稱(chēng):貨幣單位?屬性值:人民幣
????????內(nèi)容是:?150
元素:?[價(jià)格]?解析結(jié)束!
元素:?[購(gòu)買(mǎi)日期]?開(kāi)始解析!
????????內(nèi)容是:?2000,1,24
元素:?[購(gòu)買(mǎi)日期]?解析結(jié)束!
元素:?[圖書(shū)]?解析結(jié)束!
前綴映射:?book?開(kāi)始!??它的URI是:http://javausr.com
元素:?[book:文學(xué)書(shū)籍]?開(kāi)始解析!
元素:?[book:文學(xué)書(shū)籍]?解析結(jié)束!
前綴映射:?book?結(jié)束!
元素:?[歷史書(shū)籍]?開(kāi)始解析!
元素:?[歷史書(shū)籍]?解析結(jié)束!
元素:?[我的書(shū)架]?解析結(jié)束!
*******解析文檔結(jié)束*******
?
一些值得注意的問(wèn)題
首先是有關(guān)元素內(nèi)容的問(wèn)題,在SAX?API定義中元素內(nèi)容可以在一次事件(由characters()方法處理)中返回,也可以在多次事件中返回,這樣我們就應(yīng)該考慮不能一次得到所有內(nèi)容數(shù)據(jù)的情況。一般的解決辦法是定義一個(gè)StringBuffer由它來(lái)保存內(nèi)容數(shù)據(jù),在元素結(jié)束或者新元素開(kāi)始的時(shí)候清空這個(gè)StringBuffer從而可以保存新的內(nèi)容數(shù)據(jù)。請(qǐng)參考上面的相應(yīng)的源代碼。
還有在SAX?API中特意提到從?characters(char[]?ch,int?start,int?length)方法中提取數(shù)據(jù)時(shí)一定不要從返回的字符數(shù)組范圍之外讀取,這一點(diǎn)我們也要切記。
另一個(gè)值得注意的問(wèn)題是,在?startElement()方法中返回的Attributes屬性列表中的屬性順序并沒(méi)有被特意規(guī)定,在不同的SAX實(shí)現(xiàn)中也各不相同。所以我們?cè)诰帉?xiě)程序時(shí)不要把屬性順序想成一定的。
SAX與DOM的比較
通過(guò)上面的介紹我想大家對(duì)SAX已經(jīng)有了一個(gè)基本的了解。每一個(gè)進(jìn)行XML開(kāi)發(fā)的編程人員都知道DOM,那為什么在有了DOM這個(gè)功能強(qiáng)大的文檔對(duì)象模型之后,我們還需要SAX?這就要從它們根本不同的實(shí)現(xiàn)方法上來(lái)分析。DOM解析器是通過(guò)將XML文檔解析成樹(shù)狀模型并將其放入內(nèi)存來(lái)完成解析工作的,而后對(duì)文檔的操作都是在這個(gè)樹(shù)狀模型上完成的。這個(gè)在內(nèi)存中的文檔樹(shù)將是文檔實(shí)際大小的幾倍。這樣做的好處是結(jié)構(gòu)清除、操作方便,而帶來(lái)的麻煩就是極其耗費(fèi)系統(tǒng)資源。而SAX正好克服了DOM的缺點(diǎn)。SAX解析器的處理過(guò)程是通讀整個(gè)文檔,根據(jù)文檔內(nèi)容產(chǎn)生事件,而把對(duì)這些事件的處理交由事件處理器處理。SAX不需要在內(nèi)存中保存整個(gè)文檔,它對(duì)系統(tǒng)資源的節(jié)省是顯而易見(jiàn)的。這樣在一些需要處理大型XML文檔和性能要求比較高的場(chǎng)合就要用SAX了。
下面的表格列出了SAX和DOM在一些方面的對(duì)照:?SAX?DOM?
順序讀入文檔并產(chǎn)生相應(yīng)事件,可以處理任何大小的XML文檔?在內(nèi)存中創(chuàng)建文檔樹(shù),不適于處理大型XML文檔。?
只能對(duì)文檔按順序解析一遍,不支持對(duì)文檔的隨意訪問(wèn)。?可以隨意訪問(wèn)文檔樹(shù)的任何部分,沒(méi)有次數(shù)限制。?
只能讀取XML文檔內(nèi)容,而不能修改?可以隨意修改文檔樹(shù),從而修改XML文檔。?
開(kāi)發(fā)上比較復(fù)雜,需要自己來(lái)實(shí)現(xiàn)事件處理器。?易于理解,易于開(kāi)發(fā)。?
對(duì)開(kāi)發(fā)人員而言更靈活,可以用SAX創(chuàng)建自己的XML對(duì)象模型。?已經(jīng)在DOM基礎(chǔ)之上創(chuàng)建好了文檔樹(shù)。?
通過(guò)對(duì)SAX和DOM的分析,它們各有自己的不同應(yīng)用領(lǐng)域:?
SAX適于處理下面的問(wèn)題:?
對(duì)大型文檔進(jìn)行處理。?
只需要文檔的部分內(nèi)容,或者只需要從文檔中得到特定信息。?
想創(chuàng)建自己的對(duì)象模型的時(shí)候。
DOM適于處理下面的問(wèn)題:?
需要對(duì)文檔進(jìn)行修改?
需要隨機(jī)對(duì)文檔進(jìn)行訪問(wèn),例如XSLT解析器。
對(duì)SAX的介紹到這里就告一段落了,希望能對(duì)大家有所幫助:),本文的絕大部分參考資料都來(lái)源于http://www.megginson.com/SAX/?以及SAX?API(雖然說(shuō)SAX有了自己新的網(wǎng)站http://sax.sourceforge.net/?但我從來(lái)沒(méi)有成功訪問(wèn)過(guò)?。?,感謝David?Megginson和其他SAX開(kāi)發(fā)人員給我們提供了這么一個(gè)好東東。本文如有錯(cuò)誤和不妥的地方還請(qǐng)大家指正。