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

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

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

    Vincent.Chan‘s Blog

    常用鏈接

    統(tǒng)計

    積分與排名

    網(wǎng)站

    最新評論

    SAX 的基本概要

    SAX和DOM一樣也是一個存取XML文件的介面。SAX是Simple API for XML的縮寫。它不像DOM那樣是W3C的認(rèn)可標(biāo)準(zhǔn)。它是由XML-DEV信件清單的成員開發(fā)維護(hù),由David Megginson領(lǐng)導(dǎo)(david@megginson.com)的一個Public Domain軟體。SAX是一個徹底的自由軟體,它的作者放棄了對它的所有權(quán)利,它也被許可用於任何目的(在文章最後附錄了它的版權(quán)聲明)。

    到現(xiàn)在為止SAX的版本已經(jīng)發(fā)展到2.0。在這個最新版本中增加了對命名空間(Namespaces)的支援,而且可以透過對features以及 properties的設(shè)定來對剖析器做全面的配置,這其中包括設(shè)定剖析器是否對文件進(jìn)行有效性驗證,以及怎樣來處理帶有命名空間的元素名稱等。SAX1 中的介面已經(jīng)不再使用了,這裡只會討論有關(guān)SAX2的開發(fā)。在本文中提到SAX只是指SAX 2。另外,本文的所有例子都是用java編寫,SAX剖析器也使用的是JAVA版本。

    像DOM一樣,SAX並不是一個可以實際使用的XML文件剖析器,而是其它相容SAX的剖析器所要實作的介面和幫助性類別的集合。如果你想使用SAX的話,你必須滿足下面的要求︰

    1. 系統(tǒng)中包括Java 1.1 或者更高版本。
    2. 在Java classpath中匯入你的SAX類別庫。
    3. 在Java classpath中匯入相容SAX的XML剖析器類別庫。

    有很多實作SAX的剖析器,比如Apache的Xerces,Oracle的XML Parser等等。在本文中的例子使用的是Xerces剖析器,你可以從 http://xml.apache.org 取得。下載xerces.jar檔案然後加入到classpath中,這樣就建立好環(huán)境(在xerces.jar中已經(jīng)包括了SAX介面,所以不必特意再去尋找SAX類別庫)。

    在SAX API中有兩個套件,org.xml.sax和org.xml.sax.helper。其中org.xml.sax主要定義了SAX的一些基礎(chǔ)介面,如 XMLReader、ContentHandler、ErrorHandler、DTDHandler、EntityResolver等。而在 org.xml.sax.helper中則是一些方便工作人員使用的幫助性類別,如預(yù)設(shè)實作所有處理器介面的幫助性類別DefaultHandler、方 便工作人員建立XMLReader的XMLReaderFactory類別等等。在這兩個套件中還有一些應(yīng)用於SAX1的介面,同時還有幾個類別它們只是 為了便於將在SAX1上開發(fā)的應(yīng)用移植到SAX2上,在這篇文章中就不涉及了。下面是我們要關(guān)注的介面和類別︰

    Package org.xml.sax介紹
    Interfaces介面
    Attributes定義了一個屬性清單介面,供存取元素的屬性清單而用。
    ContentHandler處理剖析文件內(nèi)容時產(chǎn)生的事件。
    DTDHandler處理剖析DTD時的相對應(yīng)事件。
    EntityResolver處理外部實體。
    ErrorHandler處理剖析過程中所遇到的文件錯誤事件。
    Locator為了搜尋剖析中產(chǎn)生的內(nèi)容事件在文件中的位置而準(zhǔn)備的一個搜尋器介面。
    XMLFilter提供了一個方便應(yīng)用開發(fā)的過濾器介面。
    XMLReader任何相容SAX2的剖析器都要實作這個介面,這個介面讓應(yīng)用程式可以設(shè)定或?qū)ふ襢eatures和properties,註冊各種事件處理器,以及開始 剖析文件。
    Classes類別
    InputSource為XML實體準(zhǔn)備的來源。
    Exceptions例外
    SAXException包裝了一般的SAX錯誤和警告。
    SAXNotRecognizedException為識別不出某些識別資料而拋出的例外處理。
    SAXNotSupportedException為不支援某個作業(yè)而拋出的例外處理。
    SAXParseException包裝了一個關(guān)於XML剖析的錯誤或者警告。
    Package org.xml.sax.helpers幫助性類別所在的套件
    Classes類別
    AttributesImpl對Attributes介面的預(yù)設(shè)實作
    NamespaceSupport提供命名空間支援。
    DefaultHandler預(yù)設(shè)實作了四個處理器介面,方便使用者開發(fā),在開發(fā)過程中會經(jīng)常用到。
    LocatorImpl提供了一個對Locator介面的實作
    XMLFilterImpl對過濾器介面的實作,使用過濾器進(jìn)行應(yīng)用程式開發(fā)時,繼承這個類別很方便。
    XMLReaderFactory為方便建立不同的XMLReader而提供。也會經(jīng)常用到。

    理解並使用SAX

    SAX的設(shè)計實作與DOM是完全不同的﹗DOM處理XML文件是將XML文件剖析成樹狀模型,放入記憶體進(jìn)行處理。而SAX則是採用以事件為基礎(chǔ)的 處理模式,它將XML文件轉(zhuǎn)化成一系列的事件,由單獨的事件處理器來決定如何處理。為了瞭解如何使用SAX API處理XML文件,這裡先介紹一下SAX所使用的以事件為基礎(chǔ)的處理模式

    這種以事件為基礎(chǔ)的處理模式是一種通用的程式設(shè)計模式,被廣泛應(yīng)用於GUI設(shè)計。在JAVA的AWT,SWING以及JAVA BEANS中就有它的身影。而SAX的以事件為基礎(chǔ)的處理模式就與上面三者非常相像。

    以事件為基礎(chǔ)的處理模式主要是圍繞著事件來源以及事件處理器來工作的。一個可以產(chǎn)生事件的物件被稱為事件來源,而可以針對事件產(chǎn)生回應(yīng)的物件就被叫 做事件處理器。事件來源和事件處理器是透過在事件來源中的事件處理器註冊方法連繫的。這樣當(dāng)事件來源產(chǎn)生事件後,呼叫事件處理器相對應(yīng)的處理方法來處理一 個事件。當(dāng)然在事件來源呼叫事件處理器中特定方法的時候,會傳遞給事件處理器相對應(yīng)的狀態(tài)訊息,這樣事件處理器才能夠根據(jù)事件訊息來決定自己的行為。

    在SAX介面中,事件來源是org.xml.sax套件中的XMLReader,它透過parse()方法來開始剖析XML文件並根據(jù)文件內(nèi)容產(chǎn)生 事件。而事件處理器則是org.xml.sax套件中的ContentHandler,DTDHandler,ErrorHandler,以及 EntityResolver這四個介面。它們分別處理事件來源在剖析過程中產(chǎn)生的不同種類的事件(其中DTDHandler是為剖析文件DTD時而 用)。而事件來源XMLReader和這四個事件處理器的連繫是透過在XMLReader中所 對應(yīng)的事件處理器註冊方法set***()來完成的。詳細(xì)介紹請見下表︰

    處理器名稱所處理事件註冊方法
    org.xml.sax.ContentHandler跟文件內(nèi)容有關(guān)的所有事件︰
    # 文件的開始和結(jié)束
    # XML元素的開始和結(jié)束
    # 可忽略的實體
    # 命名空間字首對應(yīng)的開始和結(jié)束
    # 處理指令
    # 字元資料和可忽略的空格
    XMLReader中的setContentHandler(ContentHandler handler)方法
    org.xml.sax.ErrorHandler處理XML文件剖析時產(chǎn)生的錯誤。如果一個應(yīng)用程式?jīng)]有註冊一個錯誤處理器類別,會發(fā)生不可預(yù)料的剖析器行為。setErrorHandler(ErrorHandler handler)
    org.xml.sax.DTDHandler處理對文件DTD進(jìn)行剖析時產(chǎn)生的相對應(yīng)事件setDTDHandler(DTDHandler handler)
    org.xml.sax.EntityResolver處理外部實體setEntityResolver(EntityResolver resolver)

    在這四個處理器介面中,對我們最重要的是ContentHandler介面。下面讓我們看一下對其中方法的說明︰

    方法名稱方法說明
    public void setDocumentLocator(Locator locator)設(shè)定一個可以搜尋事件發(fā)生位置的搜尋器物件
    public void startDocument() throws SAXException用於處理剖析文件開始事件
    public void endDocument() throws SAXException用於處理剖析文件結(jié)束事件
    public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) throws SAXException用於處理字首對應(yīng)開始事件,從參數(shù)中可以得到字首名稱以及所指向的uri
    public void endPrefixMapping(java.lang.String prefix) throws SAXException用於處理字首對應(yīng)的結(jié)束事件,從參數(shù)中可以得到字首名稱
    public void startElement(java.lang.String namespaceURI,java.lang.String localName,java.lang.String qName,Attributes atts) throws SAXException處理元素開始事件,從參數(shù)中可以獲得元素所在命名空間的uri,元素名稱,屬性清單等訊息
    public void endElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName) throws SAXException處理元素結(jié)束事件,從參數(shù)中可以獲得元素所在命名空間的uri,元素名稱等訊息
    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中的方法,然後讓我們看一個具體的例子。XMLReader是所有相容SAX2的剖析器都要實作的介面,由它的方法開始 剖析文件,並且呼叫它的註冊方法來註冊各種事件處理器。請看下表︰

    方法名稱方法介紹
    public Boolean getFeature(java.lang.String name)throws SAXNotRecognizedException,SAXNotSupportedException得到某個feature的值
    public void setFeature(java.lang.String name,boolean value) throws SAXNotRecognizedException,SAXNotSupportedException設(shè)定某個feature的值,例如,如果需要剖析器支援對文件進(jìn)行驗證那麼就這麼呼叫本方法。myReader.setFeature(http://xml.org/sax/features/validation,true);其中myReader是XMLReader的 範(fàn)例。
    public java.lang.Object getProperty(java.lang.String name)throws SAXNotRecognizedException,SAXNotSupportedException傳回一個property的值
    public void setProperty(java.lang.String name,java.lang.Object value)throws SAXNotRecognizedException,SAXNotSupportedException設(shè)定一個property的值
    public void setEntityResolver(EntityResolver resolver)註冊處理外部實體的EntityResolver
    public EntityResolver getEntityResolver()得到系統(tǒng)中註冊的EntityResolver
    public void setDTDHandler(DTDHandler handler)註冊處理DTD剖析事件的DTDHandler
    public DTDHandler getDTDHandler()得到系統(tǒng)中註冊的DTDHandler
    public void setContentHandler(ContentHandler handler)註冊處理XML文件內(nèi)容剖析事件的ContentHandler
    public ContentHandler getContentHandler()得到系統(tǒng)中註冊的ContentHandler
    public void setErrorHandler(ErrorHandler handler)註冊處理文件剖析錯誤事件的ErrorHandler
    public ErrorHandler getErrorHandler()得到系統(tǒng)中註冊的ErrorHandler
    public void parse(InputSource input)throws java.io.IOException,SAXException開始剖析一個XML文件。
    public void parse(java.lang.String systemId)throws java.io.IOException,SAXException開始剖析一個使用系統(tǒng)識別資料符識別資料的XML文件。這個方法只是上面方法的一個捷徑它等同於︰parse(new InputSource(systemId));

    一個範(fàn)例

    讓我們透過例子來看一下使用SAX剖析XML文件的應(yīng)用程式是如何建立的。下面是在應(yīng)用程式中被處理的XML文件。為了說明SAX對命名空間的支援,我在這裡特意加了一個有命名空間的元素,在這裡會產(chǎn)生相對應(yīng)的字首對應(yīng)開始和結(jié)束事件。

    <?xml version="1.0" encoding="GB2312"?>
    <我的書架 >
    <技術(shù)書籍>
    <圖書>
    <書名>JAVA 2程式化詳解</書名>
    <價格 貨幣單位="新臺幣">150</價格>
    <購買日期>2000,1,24</購買日期>
    </圖書>
    </技術(shù)書籍>
    <book:文學(xué)書籍 xmlns:book="http://javausr.com"/>
    <歷史書籍/>
    </我的書架>

    這裡的範(fàn)例程式只是簡單地將遇到的事件訊息列印出來。我們首先實作ContentHandler介面來處理在XML文件剖析過程中產(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 {

    privateStringBuffer buf;

    public void setDocumentLocator( Locator locator ) {
    }

    public void startDocument() throws SAXException {
    buf=newStringBuffer();
    System.out.println("*******開始剖析文件*******");
    }

    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字首對應(yīng): " + prefix +" 開始!"+ " 它的URI是:" + uri);
    }

    public void endPrefixMapping( String prefix ) {
    System.out.println("\n字首對應(yīng): "+prefix+" 結(jié)束!");
    }

    public void startElement( String namespaceURI, String localName,
    String fullName, Attributes attributes )
    throws SAXException {
    System.out.println("\n 元素: " + "["+fullName+"]" +" 開始剖析!");
    // 列印出屬性訊息
    for ( int i = 0; i < attributes.getLength(); i++ ) {
    System.out.println("\t屬性名稱:" + 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 {
    }
    }

    下面讓我們建立一個引用了xerces剖析器來實作XMLReader介面、並使用剛才建立的MyContentHandler來處理對應(yīng)剖析事件的MySAXApp.java類別︰

    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 {

    publicstatic 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") ;

    // 建立ContentHandler的範(fàn)例
    ContentHandler contentHandler = new MyContentHandler();

    // 在reader中註冊實體化的ContentHandler
    reader.setContentHandler( contentHandler );

    // 開始剖析文件
    reader.parse(args[0]);

    } catch ( IOException e ) {
    System.out.println("讀入文件時錯: " + e.getMessage());
    } catch ( SAXException e ) {
    System.out.println("剖析文件時錯: " + e.getMessage());
    }
    }
    }

    上面就是使用SAX剖析一個XML文件的基本過程,但是MyContentHandler只是處理了剖析過程中和文件內(nèi)容相關(guān)的事件,如果在剖析過 程中出現(xiàn)了錯誤那我們需要實作ErrorHandler介面來處理。如果不註冊一個錯誤處理器來處理的話,那麼錯誤事件將不會被報告,而且剖析器會出現(xiàn)不 可預(yù)知的行為。在剖析過程中產(chǎn)生的錯誤被分成了3類,它們分別是warning,error,以及fatalerror,也就是說在 ErrorHandler中有這麼三個對應(yīng)的方法來處理這些錯誤事件。下面是對這三個錯誤處理方法的介紹︰

    方法名稱方法介紹
    warning()SAX剖析器將用這個方法來報告在XML1.0規(guī)範(fàn)中定義的非錯誤(error)或者致命錯誤(fatal error)的錯誤狀態(tài)。對這個錯誤預(yù)設(shè)的行為是什麼也不做。SAX剖析器必須在呼叫這個方法後繼續(xù)提供正常的剖析事件︰應(yīng)用程式應(yīng)該能繼續(xù)處理完文件。
    error()這個方法對應(yīng)在W3C XML 1.0規(guī)範(fàn)的1.2部分中定義的"error"概念。例如,一個帶有有效性驗證的剖析器會使用這個方法來報告違反有效性驗證的情況。一個帶有有效性驗證的 剖析器會使用這個方法來報告違背有效性條件的情況。預(yù)設(shè)的行為是什麼也不做。SAX剖析器必須在呼叫這個方法後繼續(xù)提供正常的剖析事件︰應(yīng)用程式應(yīng)該能繼 續(xù)處理完文件。如果應(yīng)用程式做不到這樣,則 剖析器即使在XML1.0規(guī)範(fàn)沒有要求的情況下也要報告一個致命錯誤。
    fatalError()這個方法對應(yīng)在W3C XML1.0規(guī)範(fàn)的1.2部分定義的"fatal error"概念。例如,一個剖析器會使用這個方法來報告格式錯誤。在剖析器呼叫這個方法後應(yīng)用程式必須表明這個文件是不可使用的,而且應(yīng)該 只是為了收集錯誤訊息而繼續(xù)進(jìn)行處理(如果需要的話)︰實際上,一旦在這個方法被呼叫後SAX剖析器可以停止報告任何事件。

    下面是實作了ErrorHandler介面的MyErrorHandler.java類別︰

    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錯誤訊息:\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錯誤訊息:\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錯誤訊息:\t" + exception.getMessage());
    System.out.println("*****************************");
    }
    }

    讓我們製造些錯誤來檢查一下我們的錯誤處理器工作情況。刪除元素<購買日期>的結(jié)束標(biāo)籤,這樣會產(chǎn)生一個fatal error,下面是執(zhí)行結(jié)果︰
    D:\sax\classes>java com.javausr.saxexample.MySAXApp d:\book.xml

    *******開始剖析文件*******

    元素: [我的書架] 開始剖析!

    元素: [技術(shù)書籍] 開始剖析!

    元素: [圖書] 開始剖析!

    元素: [書名] 開始剖析!

    元素: [書名] 開始剖析!
    內(nèi)容是: JAVA 2程式化詳解
    元素: [書名] 剖析結(jié)束!

    元素: [價格] 開始剖析!
    屬性名稱:貨幣單位 屬性值:新臺幣
    內(nèi)容是: 150
    元素: [價格] 剖析結(jié)束!

    元素: [購買日期] 開始剖析!
    ******** FATAL ERROR ********
    行: 8
    列: 7
    錯誤訊息: The element type "購買日期" must be terminated by the matching end-tag "</購買日期>".
    *****************************
    剖析文件時錯: Stopping after fatal error: The element type "購買日期" must be terminated by the matching end-tag "</購買日期>".

    現(xiàn)在總結(jié)一下如何撰寫SAX的應(yīng)用程式。一般步驟如下︰
    實作一個或多個處理器介面(ContentHandler, ErrorHandler, DTDHandler ,or EntityResover)。
    建立一個XMLReader類別的範(fàn)例。
    在新的XMLReader範(fàn)例中透過大量的set*****() 方法註冊一個事件處理器的實體
    呼叫XMLReader的parse()方法來處理文件。

    使用DefaultHandler

    現(xiàn)在的程式是比較完整了,但還有許多可以改進(jìn)的地方。首先在我們實作的MyContentHandler.java中,你會發(fā)現(xiàn)有很多方法實際上什 麼也沒有做,但為了實作ContentHandler介面,不得不把它們寫出來,這樣很是麻煩。SAX API已經(jīng)考慮到這個問題,在它的org.xml.sax.helper套件中為我們提供了一個方便實作各種處理器介面的幫助性類別 DefaultHandler。這個類別預(yù)設(shè)實作了上面提到的4個處理器介面。這樣我們只需繼承這個類別,然後覆寫(override)我們想要實作的事 件處理方法即可。下面我們來新增一個繼承了DefaultHandler的MyDefaultHandler.java類別,然後把在 MyContentHandler.java和MyErrorHandler.java中實作的事件處理方法照搬到 MyDefaultHandler.java類別中,那些沒有使用的方法就不必重複了。這裡是MyDefaultHandler.java︰

    package com.javausr.saxexample; 
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    import java.io.*;

    public class MyDefaultHandler extends DefaultHandler {

    privateStringBuffer buf;

    public void startDocument() throws SAXException {
    buf=newStringBuffer();
    System.out.println("*******開始剖析文件*******");
    }

    public void endDocument() throws SAXException {
    System.out.println("*******剖析文件結(jié)束*******");
    }

    public void startPrefixMapping( String prefix, String uri ) {
    System.out.println("\n字首對應(yīng): " + prefix +" 開始!"+ " 它的URI是:"+uri);
    }

    public void endPrefixMapping( String prefix ) {
    System.out.println("\n字首對應(yīng): "+prefix+" 結(jié)束!");
    }

    public void startElement( String namespaceURI, String localName,
    String fullName, Attributes attributes )
    throws SAXException {

    System.out.println("\n元素: " + "["+fullName+"]" +" 開始剖析!");

    // 列印出屬性訊息
    for ( int i = 0; i < attributes.getLength(); i++ ) {
    System.out.println("\t屬性名稱:" + 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錯誤訊息:\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錯誤訊息:\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錯誤訊息:\t" + exception.getMessage());
    System.out.println("*****************************");
    }
    }

    我們也要對MySAXApp.java做相對應(yīng)的修改,修改已在原始碼中標(biāo)出︰
    package com.javausr.saxexample;

    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 {

    publicstatic 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") ;

    // 建立DefaultHandler的範(fàn)例
    DefaultHandler defaultHandler=new MyDefaultHandler();

    //在reader中將defaultHandler註冊為ContentHandler
    reader.setContentHandler(defaultHandler);

    //在reader中將defaultHandler註冊為ErrorHandler
    reader.setErrorHandler(defaultHandler);

    // 開始剖析文件
    reader.parse(args[0]);

    } catch ( IOException e ) {
    System.out.println("讀入文件時錯: " + e.getMessage());
    } catch ( SAXException e ) {
    System.out.println("剖析文件時錯: " + e.getMessage());
    }
    }
    }

    使用過濾器

    在SAX API中還提供了一個過濾器介面org.xml.sax.XMLFilter,以及對它的預(yù)設(shè)實作 org.xml.sax.helper.XMLFilterImpl。使用它們可以很容易的開發(fā)出複雜的SAX應(yīng)用。這裡要先介紹一下過濾器設(shè)計模式。這 個設(shè)計模式很好理解,就像一個淨(jìng)化水的過程。自然界中的水流過一個個的過濾器得到最後的飲用水。這些過濾器,有的是清除水中的泥沙,有的是消滅水中的細(xì) 菌,總之不同的過濾器完成不同的工作。在應(yīng)用開發(fā)中,我們讓被改造的物件(這裡是事件串)透過這些過濾器物件從而得到改造後符合要求的物件。這樣,在過濾 器的幫助之下,我們可以非常方便的在每個過濾器中實作一個特定功能,從而建立結(jié)構(gòu)複雜的應(yīng)用程式。在應(yīng)用程式中你可以構(gòu)造任意多個過濾器,將它們串接起來 完成工作。

    在SAX API中org.xml.sax.XMLFilter介面繼承了org.xml.sax.XMLReader介面。它與XMLReader不同的是它不像 XMLReader那樣透過剖析文件來獲取事件,而是從其它XMLReader中獲取事件,當(dāng)然這也包括從其它的XMLFilter中獲取事件。在 org.xml.sax.XMLFilter中有兩個方法︰

    方法名稱方法描述
    Public void setParent(XMLReader parent)設(shè)定父XMLReader。這個方法讓應(yīng)用程式將這個過濾器取得到它的父XMLReader (也可能是另一個過濾器)。
    Public XMLReader getParent()獲取父XMLReader。這個方法讓應(yīng)用程式可以查詢父XMLReader(也可能是另一個過濾器)。最好不要在父XMLReader中直接進(jìn)行任何作業(yè)︰讓所有的事件透過這個過濾器來處理。

    我們不需要自己實作org.xml.sax.XMLFilter介面,在SAX API 中提供了一個org.xml.sax.helper.XMLFilterImpl類別,它不只有實作了org.xml.sax.XMLFilter介面而 且還實作了其它四個核心處理器介面,我們只需要繼承它即可完成我們的過濾器。剛開始使用XMLFilterImpl比較容易讓人迷惑,你只需要記住︰

    1. 在你繼承的XMLFilterImpl類別中用set****()方法註冊的事件處理器是給過濾後的事件串用的。
    2. 在 你繼承的XMLFilterImpl類別中實作的那些事件處理方法,比如startDocument()、startElement()、 characters()等 才是這個過濾器實作它自身功能的地方。而透過繼承XMLFilterImpl而實作的這個類別會被轉(zhuǎn)型成各種處理器(它本身實作了四個處理器介面)用在它 的父XMLReader中。這個步驟會在你呼叫自己建立的過濾器的parse()方法開始 剖析文件時被自動執(zhí)行(請參見SAX原始碼)。
    3. 如果不是使用帶參數(shù)的建構(gòu)子建立XMLFilter物件,務(wù)必使用setParent(XMLReader parent)方法取得它的父XMLReader。
    4. 果使用多個過濾器的話,執(zhí)行順序是從一開始到最後的過濾器。但是開始剖析卻要呼叫最後一個過濾器的parse()方法。

    下面讓我們結(jié)合已有的例子來展示過濾器org.xml.sax.XMLFilter的作用。我們在這個過濾器中要過濾掉<技術(shù)書籍>這個元素,最後得到的事件串還是由上面實作的MyDefaultHandler來處理。原始碼如下MyFilter.java︰

    package com.javausr.saxexample; 
    import org.xml.sax.*;
    import org.xml.sax.helpers.*;
    import java.io.*;

    public class MyFilter extends XMLFilterImpl {

    privateString currentElement;

    public MyFilter( XMLReader parent ) {
    super(parent);
    }

    /**
    * 過濾掉元素<技術(shù)書籍>的開始事件
    **/
    public void startElement( String namespaceURI, String localName,
    String fullName, Attributes attributes )
    throws SAXException {

    currentElement = localName;
    if ( !localName.equals("技術(shù)書籍") ) {
    super.startElement(namespaceURI, localName, fullName, attributes);
    }
    }

    /**
    * 過濾掉元素<技術(shù)書籍>的結(jié)束事件
    **/
    public void endElement(String namespaceURI, String localName, String
    fullName)
    throws SAXException {

    if ( !localName.equals("技術(shù)書籍") ) {
    super.endElement(namespaceURI, localName, fullName);
    }
    }

    /**
    * 過濾掉元素<技術(shù)書籍>中的內(nèi)容
    **/
    public void characters(char[] buffer, int start, int length)
    throws SAXException {
    if ( !currentElement.equals("技術(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 {

    publicstatic 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") ;

    //初始化過濾器
    XMLFilter myFilter=new MyFilter(reader);

    // 建立DefaultHandler的範(fàn)例
    DefaultHandler defaultHandler=new MyDefaultHandler();

    //為過濾後的事件流設(shè)定ContentHandler
    myFilter.setContentHandler(defaultHandler);

    //為過濾後的事件流設(shè)定ErrorHandler
    myFilter.setErrorHandler(defaultHandler);


    // 開始剖析文件,注意是使用myFilter中的剖析方法
    myFilter.parse(args[0]);

    } catch ( IOException e ) {
    System.out.println("讀入文件時錯: " + e.getMessage());
    } catch ( SAXException e ) {
    System.out.println("剖析文件時錯: " + e.getMessage());
    }
    }
    }

    這裡是最後的執(zhí)行結(jié)果,我們可以發(fā)現(xiàn)有關(guān)<技術(shù)書籍>的全部事件已經(jīng)被過濾掉了。認(rèn)真看一下結(jié)果,你一定覺得奇怪,為什麼<技術(shù) 書籍>元素的子元素仍然存在。請記住SAX是把XML文件剖析成事件流,所有沒有被過濾的事件都會保留下來。這就是SAX和DOM的最大不同。在 DOM中文件被剖析成了樹狀模型,如果你刪除一個元素,那麼這個元素以及它的子元素就都會被刪除,這符合樹狀模型的特點。

    D:\sax\classes>java com.javausr.saxexample.MySAXApp d:\book.xml

    *******開始剖析文件*******

    元素: [我的書架] 開始剖析!

    元素: [圖書] 開始剖析!

    元素: [書名] 開始剖析!
    內(nèi)容是: JAVA 2程式化詳解
    元素: [書名] 剖析結(jié)束!

    元素: [價格] 開始剖析!
    屬性名稱:貨幣單位 屬性值:新臺幣
    內(nèi)容是: 150
    元素: [價格] 剖析結(jié)束!

    元素: [購買日期] 開始剖析!
    內(nèi)容是: 2000,1,24
    元素: [購買日期] 剖析結(jié)束!
    元素: [圖書] 剖析結(jié)束!

    字首對應(yīng): book 開始! 它的URI是:http://javausr.com

    元素: [book:文學(xué)書籍] 開始剖析!
    元素: [book:文學(xué)書籍] 剖析結(jié)束!

    字首對應(yīng): book 結(jié)束!

    元素: [歷史書籍] 開始剖析!
    元素: [歷史書籍] 剖析結(jié)束!
    元素: [我的書架] 剖析結(jié)束!
    *******剖析文件結(jié)束*******

    一些值得注意的問題

    首先是有關(guān)元素內(nèi)容的問題,在SAX API定義中元素內(nèi)容可以在一次事件(由characters()方法處理)中傳回,也可以在多次事件中傳回,這樣我們就應(yīng)該考慮不能一次得到所有內(nèi)容資 料的情況。一般的解決辦法是定義一個StringBuffer由它來儲存內(nèi)容資料,在元素結(jié)束或者新元素開始的時候清空這個StringBuffer從而 可以儲存新的內(nèi)容資料。請參考上面的原始碼。

    還有在SAX API中特意提到從 characters(char[] ch,int start,int length)方法中取資料時一定不要從傳回的字元數(shù)組範(fàn)圍之外讀取,這一點我們也要切記。

    另一個值得注意的問題是,在 startElement()方法中傳回的Attributes屬性清單中的屬性順序並沒有被特意規(guī)定,在不同的SAX實作中也各不相同。所以我們在編寫程式時不要把屬性順序想成一定的。

    SAX與DOM的比較

    透過上面的介紹我想大家對SAX已經(jīng)有了一個基本的瞭解。每一個進(jìn)行XML開發(fā)的程式化人員都知道DOM,那為什麼在有了DOM這個功能強(qiáng)大的文件 物件模型之後,我們還需要SAX?這就要從它們不同的實作方法上來分析。DOM剖析器是透過將XML文件剖析成樹狀模型並將其放入記憶體來完成剖析工作 的,而後對文件的作業(yè)都是在這個樹狀模型上完成的。這個在記憶體中的文件樹將是文件實際大小的幾倍。這樣做的好處是結(jié)構(gòu)清楚、作業(yè)方便,而帶來的麻煩就是 極其耗費(fèi)系統(tǒng)資源。而SAX正好克服了DOM的缺點。SAX剖析器的處理過程是通讀整個文件,根據(jù)文件內(nèi)容產(chǎn)生事件,而把對這些事件的處理交由事件處理器 處理。SAX不需要在記憶體中儲存整個文件,它對系統(tǒng)資源的節(jié)省是顯而易見的。這樣在一些需要處理大型XML文件和效能要求比較高的場合就要用SAX了。

    下面的表格列出了SAX和DOM在一些方面的對照︰

    SAXDOM
    依序讀入文件並產(chǎn)生相對應(yīng)事件,可以處理任何大小的XML文件在記憶體中建立文件樹,不適於處理大型XML文件。
    只能對文件按順序剖析一遍,不支援對文件的隨意存取。可以隨意存取文件樹的任何部分,沒有次數(shù)限制。
    只能讀取XML文件內(nèi)容,而不能修改可以隨意修改文件樹,從而修改XML文件。
    開發(fā)上比較複雜,需要自己來實作事件處理器。易於理解,易於開發(fā)。
    對工作人員而言更靈活,可以用SAX建立自己的XML物件模型。已經(jīng)在DOM基礎(chǔ)之上建立好了文件樹。

    透過對SAX和DOM的分析,它們各有自己的不同應(yīng)用領(lǐng)域︰
    SAX適於處理下面的問題︰

    1. 對大型文件進(jìn)行處理。
    2. 只需要文件的部分內(nèi)容,或者只需要從文件中得到特定訊息。
    3. 想建立自己的物件模型的時候。

    DOM適於處理下面的問題︰

    1. 需要對文件進(jìn)行修改
    2. 需要隨機(jī)對文件進(jìn)行存取,例如XSLT剖析器。

    對SAX的介紹到這裡就告一段落了,希望能對大家有所幫助︰),本文的絕大部分參考資料都來於http: //www.megginson.com/SAX/ 以及SAX API(雖然說SAX有了自己新的網(wǎng)站http://sax.sourceforge.net/ 但我從來沒有成功存取過﹗) ,感謝David Megginson和其它SAX工作人員給我們提供了這麼一個好東西。本文如有錯誤和不妥的地方還請大家指正。

    附錄: SAX的版權(quán)聲明
    SAX2 is Free!

    I hereby abandon any property rights to SAX 2.0 (the Simple API for XML), and release all of the SAX 2.0 source code, compiled code, and documentation contained in this distribution into the Public Domain. SAX comes with NO WARRANTY or guarantee of fitness for any purpose.

    David Megginson, david@megginson.com

    2000-05-05

    關(guān)於作者
    王曉強(qiáng),萬千程式開發(fā)者中的一員,並樂在其中。熱愛java和linux,一直利用java和xml相關(guān)技術(shù)進(jìn)行應(yīng)用開發(fā),並在這些方面積累了豐富經(jīng)驗。可透過 forff@sina.com 與他連絡(luò)。

    posted on 2006-03-22 23:55 Vincent.Chen 閱讀(592) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 四虎国产精品免费永久在线| 亚洲性日韩精品一区二区三区 | 最新亚洲成av人免费看| 亚洲小视频在线播放| ZZIJZZIJ亚洲日本少妇JIZJIZ| 在线a免费观看最新网站| 国产乱妇高清无乱码免费| 亚洲色大成网站www永久男同| 亚洲VA中文字幕无码一二三区| 国产免费午夜a无码v视频| 99亚洲精品高清一二区| 亚洲精品免费网站| 亚洲精品123区在线观看| 亚洲AV成人片色在线观看高潮| 亚洲日韩中文无码久久| 国产jizzjizz免费看jizz| 无码高潮少妇毛多水多水免费| 69pao强力打造免费高清| 亚洲视频在线观看免费| a级成人毛片免费视频高清| www成人免费观看网站| 小说区亚洲自拍另类| 亚洲高清国产拍精品熟女| 亚洲人成片在线观看| 亚洲精品美女在线观看播放| 亚洲AV综合色区无码一区| 丝袜熟女国偷自产中文字幕亚洲| 日韩免费视频播播| 在线播放免费人成视频在线观看| 免费国产黄线在线观看| 国产成人午夜精品免费视频| 67194成手机免费观看| 亚洲三级在线免费观看| 思思re热免费精品视频66| 成人免费的性色视频| 亚洲免费福利在线视频| 毛色毛片免费观看| 日本成人在线免费观看| 男人的天堂亚洲一区二区三区 | 成人免费无码精品国产电影| 精品免费久久久久久成人影院|