<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0
    這里我將向大家介紹處理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)大家指正。

    posted on 2006-12-22 20:35 SIMONE 閱讀(951) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JAVA
    主站蜘蛛池模板: 国产亚洲老熟女视频| 亚洲一区二区三区乱码A| 日本免费一二区在线电影| 国产精品二区三区免费播放心 | 五月婷婷在线免费观看| 动漫黄网站免费永久在线观看 | 91精品全国免费观看含羞草| 国产桃色在线成免费视频 | 午夜dj免费在线观看| 四虎影视精品永久免费| 在线观看亚洲成人| 久久久久亚洲AV无码永不| 在线观看亚洲AV日韩A∨| 永久免费观看黄网站| 色猫咪免费人成网站在线观看| 美女视频黄的全免费视频| 免费在线观看黄网站| 久久综合九九亚洲一区| 久久乐国产综合亚洲精品| 色老头综合免费视频| 6080午夜一级毛片免费看6080夜福利 | 国产美女无遮挡免费网站| 国产av无码专区亚洲av果冻传媒| 亚洲综合视频在线| 蜜芽亚洲av无码一区二区三区| 精品国产福利尤物免费| 台湾一级毛片永久免费| 国产精品亚洲二区在线观看| 亚洲狠狠ady亚洲精品大秀| 国产亚洲精品第一综合| 亚洲午夜免费视频| 国产精品麻豆免费版| 亚洲AV乱码久久精品蜜桃| 亚洲sm另类一区二区三区| 日本不卡免费新一区二区三区| 在线a毛片免费视频观看| 亚洲av无码国产精品夜色午夜| 亚洲人成77777在线播放网站不卡| 一个人看的免费视频www在线高清动漫 | 免费人成动漫在线播放r18 | 国产L精品国产亚洲区久久|