• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            為生存而奔跑

               :: 首頁 :: 聯系 :: 聚合  :: 管理
              271 Posts :: 0 Stories :: 58 Comments :: 0 Trackbacks

            留言簿(5)

            我參與的團隊

            搜索

            •  

            積分與排名

            • 積分 - 327087
            • 排名 - 74

            最新評論

            閱讀排行榜

            評論排行榜

            級別: 初級

            王曉強 (forff@sina.com), 

            2001 年 11 月 01 日

            這里我將向大家介紹處理 XML 文檔的另一個重要接口 SAX(Simple API for XML)。其中包括它的基本情況,它的 API,一個開發實例,實際開發中一些需注意的問題,以及它與 DOM 的對比。

            SAX的基本情況

            SAX同DOM一樣也是一個訪問XML文檔的接口。SAX是Simple API for XML的縮寫。它不像DOM那樣是W3C的推薦標準。它是由XML-DEV郵件列表的成員開發維護,由David Megginson領導(david@megginson.com)的一個Public Domain軟件。SAX是一個徹底的自由軟件,它的作者放棄了對它的所有權利,并且它也被許可用于任何目的(在文章最后附錄了它的版權聲明)。

            到現在為止SAX的版本已經發展到2.0。在這個最新版本中增加了對名稱空間(Namespaces)的支持,而且可以通過對features以及properties的設置來對解析器做全面的配置,這其中包括設置解析器是否對文檔進行有效性驗證,以及怎樣來處理帶有名稱空間的元素名稱等。SAX1中的接口已經不再使用了,這里只會討論有關SAX2的開發。在本文中提到SAX只是指SAX 2。另外,本文的所有例子都是用java編寫,SAX解析器也使用的是JAVA版本。

            像DOM一樣,SAX并不是一個實際可以使用的XML文檔解析器,而是其他兼容SAX的解析器要實現的接口和幫助類的集合。如果你想使用SAX的話,你必須滿足下面的要求:

            1. 系統中包含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中去,這樣我們就已經建立好環境(在xerces.jar中已經包含了SAX接口,所以不必特意再去尋找SAX類庫)。

            在SAX API中有兩個包,org.xml.sax和org.xml.sax.helper。其中org.xml.sax中主要定義了SAX的一些基礎接口,如XMLReader、ContentHandler、ErrorHandler、DTDHandler、EntityResolver等。而在org.xml.sax.helper中則是一些方便開發人員使用的幫助類,如缺省實現所有處理器接口的幫助類DefaultHandler、方便開發人員創建XMLReader的XMLReaderFactory類等等。在這兩個包中還有一些應用于SAX1的接口,同時還有幾個類它們只是為了便于將在SAX1上開發的應用移植到SAX2上,在這篇文章中就不涉及了。下面是我們要關注的接口和類:

            Package org.xml.sax 介紹
            Interfaces 接口
            Attributes 定義了一個屬性列表接口,供訪問元素的屬性列表而用。
            ContentHandler 處理解析文檔內容時產生的事件。
            DTDHandler 處理解析DTD時的相應事件。
            EntityResolver 處理外部實體。
            ErrorHandler 處理解析過程中所遇到的文檔錯誤事件。
            Locator 為了定位解析中產生的內容事件在文檔中的位置而準備的一個定位器接口。
            XMLFilter 提供了一個方便應用開發的過濾器接口。
            XMLReader 任何兼容SAX2的解析器都要實現這個接口,這個接口讓應用程序可以設置或查找features和properties,注冊各種事件處理器,以及開始解析文檔。
            Classes
            InputSource 為XML實體準備的輸入源。
            Exceptions
            SAXException 包裝了一般的SAX錯誤和警告。
            SAXNotRecognizedException 為識別不出某些標識而拋出的異常。
            SAXNotSupportedException 為不支持某個操作而拋出的異常。
            SAXParseException 包裝了一個關于XML解析的錯誤或者警告。

            Package org.xml.sax.helpers 幫助類所在的包
            Classes
            AttributesImpl 對Attributes接口的缺省實現
            NamespaceSupport 提供名稱空間支持。
            DefaultHandler 缺省實現了四個處理器接口,方便用戶開發,在開發過程中會經常用到。
            LocatorImpl 提供了一個對Locator接口的實現
            XMLFilterImpl 對過濾器接口的實現,使用過濾器進行應用程序開發時,繼承這個類很方便。
            XMLReaderFactory 為方便創建不同的XMLReader而提供。也會經常用到。





            回頁首


            理解并使用SAX

            SAX的設計實現與DOM是完全不同的!DOM處理XML文檔是基于將XML文檔解析成樹狀模型,放入內存進行處理。而SAX則是采用基于事件驅動的處理模式,它將XML文檔轉化成一系列的事件,由單獨的事件處理器來決定如何處理。為了了解如何使用SAX API處理XML文檔,這里先介紹一下SAX所使用的基于事件驅動的處理模式。

            這種基于事件的處理模式是一種通用的程序設計模式,被廣泛應用于GUI設計。在JAVA的AWT,SWING以及JAVA BEANS中就有它的身影。而SAX的基于事件驅動的處理模式就與上面三者中的非常相像。

            基于事件的處理模式主要是圍繞著事件源以及事件處理器(或者叫監聽器)來工作的。一個可以產生事件的對象被稱為事件源,而可以針對事件產生響應的對象就被叫做事件處理器。事件源和事件處理器是通過在事件源中的事件處理器注冊方法連接的。這樣當事件源產生事件后,調用事件處理器相應的處理方法,一個事件就獲得了處理。當然在事件源調用事件處理器中特定方法的時候,會傳遞給事件處理器相應事件的狀態信息,這樣事件處理器才能夠根據事件信息來決定自己的行為。

            在SAX接口中,事件源是org.xml.sax包中的XMLReader,它通過parse()方法來開始解析XML文檔并根據文檔內容產生事件。而事件處理器則是org.xml.sax包中的ContentHandler,DTDHandler,ErrorHandler,以及EntityResolver這四個接口。它們分別處理事件源在解析過程中產生的不同種類的事件(其中DTDHandler是為解析文檔DTD時而用)。而事件源XMLReader和這四個事件處理器的連接是通過在XMLReader中的相應的事件處理器注冊方法set***()來完成的。詳細介紹請見下表:

            處理器名稱 所處理事件 注冊方法
            org.xml.sax.ContentHandler 跟文檔內容有關的所有事件:
            1. 文檔的開始和結束
            2. XML元素的開始和結束
            3. 可忽略的實體
            4. 名稱空間前綴映射開始和結束
            5. 處理指令
            6. 字符數據和可忽略的空格
            XMLReader中的setContentHandler(ContentHandler handler)方法
            org.xml.sax.ErrorHandler 處理XML文檔解析時產生的錯誤。如果一個應用程序沒有注冊一個錯誤處理器類,會發生不可預料的解析器行為。 setErrorHandler(ErrorHandler handler)
            org.xml.sax.DTDHandler 處理對文檔DTD進行解析時產生的相應事件 setDTDHandler(DTDHandler handler)
            org.xml.sax.EntityResolver 處理外部實體 setEntityResolver(EntityResolver resolver)

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

            方法名稱 方法說明
            public void setDocumentLocator(Locator locator) 設置一個可以定位文檔內容事件發生位置的定位器對象
            public void startDocument() throws SAXException 用于處理文檔解析開始事件
            public void endDocument() throws SAXException 用于處理文檔解析結束事件
            public void startPrefixMapping(java.lang.String prefix, java.lang.String uri) throws SAXException 用于處理前綴映射開始事件,從參數中可以得到前綴名稱以及所指向的uri
            public void endPrefixMapping(java.lang.String prefix) throws SAXException 用于處理前綴映射結束事件,從參數中可以得到前綴名稱
            public void startElement(java.lang.String namespaceURI,java.lang.String localName,java.lang.String qName,Attributes atts) throws SAXException 處理元素開始事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱,屬性列表等信息
            public void endElement(java.lang.String namespaceURI, java.lang.String localName, java.lang.String qName) throws SAXException 處理元素結束事件,從參數中可以獲得元素所在名稱空間的uri,元素名稱等信息
            public void characters(char[] ch, int start, int length) throws SAXException 處理元素的字符內容,從參數中可以獲得內容
            public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException 處理元素的可忽略空格
            public void processingInstruction(java.lang.String target, java.lang.String data) throws SAXException 處理解析中產生的處理指令事件

            這里再介紹一下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 設置某個feature的值,例如,如果需要解析器支持對文檔進行驗證那么就這么調用本方法。myReader.setFeature(http://xml.org/sax/features/validation,true);其中myReader是XMLReader的實例。
            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 設置一個property的值
            public void setEntityResolver(EntityResolver resolver) 注冊處理外部實體的EntityResolver
            public EntityResolver getEntityResolver() 得到系統中注冊的EntityResolver
            public void setDTDHandler(DTDHandler handler) 注冊處理DTD解析事件的DTDHandler
            public DTDHandler getDTDHandler() 得到系統中注冊的DTDHandler
            public void setContentHandler(ContentHandler handler) 注冊處理XML文檔內容解析事件的ContentHandler
            public ContentHandler getContentHandler() 得到系統中注冊的ContentHandler
            public void setErrorHandler(ErrorHandler handler) 注冊處理文檔解析錯誤事件的ErrorHandler
            public ErrorHandler getErrorHandler() 得到系統中注冊的ErrorHandler
            public void parse(InputSource input)throws java.io.IOException,SAXException 開始解析一個XML文檔。
            public void parse(java.lang.String systemId)throws java.io.IOException,SAXException 開始解析一個使用系統標識符標識的XML文檔。這個方法只是上面方法的一個快捷方式它等同于:parse(new InputSource(systemId));





            回頁首


            一個實例

            讓我們通過例子來看一下使用SAX解析XML文檔的應用程序是如何建立的。下面是在應用程序中被處理的XML文檔。為了說明SAX對名稱空間的支持,我在這里特意加了一個有名稱空間的元素,在這里會產生相應的前綴映射開始和結束事件。

            <?xml version="1.0" encoding="GB2312"?>
                        <我的書架 >
                        <技術書籍>
                        <圖書>
                        <書名>JAVA 2編程詳解</書名>
                        <價格 貨幣單位="人民幣">150</價格>
                        <購買日期>2000,1,24</購買日期>
                        </圖書>
                        </技術書籍>
                        <book:文學書籍 xmlns:book="http://javausr.com"/>
                        <歷史書籍/>
                        </我的書架>

            這里的例子程序只是簡單地將遇到的事件信息打印出來。我們首先實現ContentHandler接口來處理在XML文檔解析過程中產生的和文檔內容相關的事件,代碼如下所示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("*******開始解析文檔*******");
                        }
                        public void endDocument() throws SAXException {
                        System.out.println("*******解析文檔結束*******");
                        }
                        public void processingInstruction( String target, String instruction )
                        throws SAXException {
                        }
                        public void startPrefixMapping( String prefix, String uri ) {
                        System.out.println("\n前綴映射: " + prefix +" 開始!"+ "  它的URI是:" + uri);
                        }
                        public void endPrefixMapping( String prefix ) {
                        System.out.println("\n前綴映射: "+prefix+" 結束!");
                        }
                        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 {
                        //打印出非空的元素內容并將StringBuffer清空
                        String nullStr="";
                        if (!buf.toString().trim().equals(nullStr)){
                        System.out.println("\t內容是: " + buf.toString().trim());
                        }
                        buf.setLength(0);
                        //打印元素解析結束信息
                        System.out.println("元素: "+"["+fullName+"]"+" 解析結束!");
                        }
                        public void characters( char[] chars, int start, int length )
                        throws SAXException {
                        //將元素內容累加到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來處理相應解析事件的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 {
                        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") ;
                        // 創建ContentHandler的實例
                        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());
                        }
                        }
                        }

            下面讓我們來看一下執行結果:

            D:\sax\classes>java com.javausr.saxexample.MySAXApp d:\book.xml
                        *******開始解析文檔*******
                        元素: [我的書架] 開始解析!
                        元素: [技術書籍] 開始解析!
                        元素: [圖書] 開始解析!
                        元素: [書名] 開始解析!
                        內容是: JAVA 2編程詳解
                        元素: [書名] 解析結束!
                        元素: [價格] 開始解析!
                        屬性名稱:貨幣單位 屬性值:人民幣
                        內容是: 150
                        元素: [價格] 解析結束!
                        元素: [購買日期] 開始解析!
                        內容是: 2000,1,24
                        元素: [購買日期] 解析結束!
                        元素: [圖書] 解析結束!
                        元素: [技術書籍] 解析結束!
                        前綴映射: book 開始!  它的URI是:http://javausr.com
                        元素: [book:文學書籍] 開始解析!
                        元素: [book:文學書籍] 解析結束!
                        前綴映射: book 結束!
                        元素: [歷史書籍] 開始解析!
                        元素: [歷史書籍] 解析結束!
                        元素: [我的書架] 解析結束!
                        *******解析文檔結束*******

            上面就是使用SAX解析一個XML文檔的基本過程,但是MyContentHandler只是處理了解析過程中和文檔內容相關的事件,如果在解析過程中出現了錯誤那我們需要實現ErrorHandler接口來處理。如果不注冊一個錯誤處理器來處理的話,那么錯誤事件將不會被報告,而且解析器會出現不可預知的行為。在解析過程中產生的錯誤被分成了3類,它們分別是warning,error,以及fatalerror,也就是說在ErrorHandler中有這么三個相應的方法來處理這些錯誤事件。下面是對這三個錯誤處理方法的介紹:

            方法名稱 方法介紹
            warning() SAX解析器將用這個方法來報告在XML1.0規范中定義的非錯誤(error)或者致命錯誤(fatal error)的錯誤狀態。對這個錯誤缺省的行為是什么也不做。SAX解析器必須在調用這個方法后繼續提供正常的解析事件:應用程序應該能繼續處理完文檔。
            error() 這個方法對應在W3C XML 1.0規范的1.2部分中定義的"error"概念。例如,一個帶有有效性驗證的解析器會使用這個方法來報告違反有效性驗證的情況。一個帶有有效性驗證的解析器會使用這個方法來報告違背有些性約束的情況。缺省的行為是什么也不做。SAX解析器必須在調用這個方法后繼續提供正常的解析事件:應用程序應該能繼續處理完文檔。如果應用程序做不到這樣,則解析器即使在XML1.0規范沒有要求的情況下也要報告一個致命錯誤。
            fatalError() 這個方法對應在W3C XML1.0規范的1.2部分定義的"fatal error"概念。例如,一個解析器會使用這個方法來報告違反格式良好約束的情況。在解析器調用這個方法后應用程序必須表明這個文檔是不可使用的,而且應該只是為了收集錯誤信息而繼續進行處理(如果需要的話):實際上,一旦在這個方法被調用后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("*****************************");
                        }
                        }

            我們也要對MySAXApp.java類做一些修改(在源代碼中藍色標出的部分)使它使用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") ;
                        // 創建ContentHandler的實例
                        ContentHandler contentHandler = new MyContentHandler();
                        // 在reader中注冊實例化的ContentHandler
                        reader.setContentHandler( contentHandler );
                        // 創建ErrorHandler的實例
                        ErrorHandler errorHandler = new MyErrorHandler();
                        // 在reader中注冊實例化的ErrorHandler
                        reader.setErrorHandler( errorHandler );
                        // 開始解析文檔
                        reader.parse(args[0]);
                        } catch ( IOException e ) {
                        System.out.println("讀入文檔時錯: " + e.getMessage());
                        } catch ( SAXException e ) {
                        System.out.println("解析文檔時錯: " + e.getMessage());
                        }
                        }

            讓我們人為制造些錯誤來檢查一下我們的錯誤處理器工作情況。刪除元素<購買日期>的閉合標記,這樣會產生一個fatal error,下面是執行結果: D:\sax\classes>java com.javausr.saxexample.MySAXApp d:\book.xml

            *******開始解析文檔*******
                        元素: [我的書架] 開始解析!
                        元素: [技術書籍] 開始解析!
                        元素: [圖書] 開始解析!
                        元素: [書名] 開始解析!
                        元素: [書名] 開始解析!
                        內容是: JAVA 2編程詳解
                        元素: [書名] 解析結束!
                        元素: [價格] 開始解析!
                        屬性名稱:貨幣單位 屬性值:人民幣
                        內容是: 150
                        元素: [價格] 解析結束!
                        元素: [購買日期] 開始解析!
                        ******** 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 "</購買日期>".
                        

            現在總結一下如何書寫基于SAX的應用程序。一般步驟如下:

            1. 實現一個或多個處理器接口(ContentHandler, ErrorHandler, DTDHandler ,or EntityResover)。
            2. 創建一個XMLReader類的實例。
            3. 在新的XMLReader實例中通過大量的set*****() 方法注冊一個事件處理器的實例
            4. 調用XMLReader的parse()方法來處理文檔。





            回頁首


            使用DefaultHandler


            現在的程序是比較完整了,但還有許多可以改進的地方。首先在我們實現的MyContentHandler.java中,你會發現有很多方法實際上什么也沒有做,但為了實現ContentHandler接口,不得不把它們寫出來,這樣很是麻煩。SAX API已經考慮到這個問題,在它的org.xml.sax.helper包中為我們提供了一個方便實現各種處理器接口的幫助類DefaultHandler。這個類缺省實現了上面提到的4個處理器接口。這樣我們只需繼承這個類,然后覆蓋我們想要實現的事件處理方法即可。下面我們來新建一個繼承了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 {
                        private StringBuffer buf;
                        public void startDocument() throws SAXException {
                        buf=new StringBuffer();
                        System.out.println("*******開始解析文檔*******");
                        }
                        public void endDocument() throws SAXException {
                        System.out.println("*******解析文檔結束*******");
                        }
                        public void startPrefixMapping( String prefix, String uri ) {
                        System.out.println("\n前綴映射: " + prefix +" 開始!"+ "  它的URI是:"+uri);
                        }
                        public void endPrefixMapping( String prefix ) {
                        System.out.println("\n前綴映射: "+prefix+" 結束!");
                        }
                        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 {
                        //打印出非空的元素內容并將StringBuffer清空
                        String nullStr="";
                        if (!buf.toString().trim().equals(nullStr)){
                        System.out.println("\t內容是: " + buf.toString().trim());
                        }
                        buf.setLength(0);
                        //打印元素解析結束信息
                        System.out.println("元素: "+"["+fullName+"]"+" 解析結束!");
                        }
                        public void characters( char[] chars, int start, int length )
                        throws SAXException {
                        //將元素內容累加到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做相應的修改,修改已在源代碼中標出: 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") ;
                        // 創建DefaultHandler的實例
                        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,以及對它的缺省實現org.xml.sax.helper.XMLFilterImpl。使用它們可以很容易的開發出復雜的SAX應用。這里要先介紹一下過濾器設計模式。這個設計模式很好理解,就像一個凈化水的過程。自然界中的水流過一個個的過濾器得到最后的飲用水。這些過濾器,有的是清除水中的泥沙,有的是殺滅水中的細菌,總之不同的過濾器完成不同的任務。在應用開發中,我們讓被改造的對象(這里是事件流)通過這些過濾器對象從而得到改造后符合要求的對象。這樣,在過濾器的幫助之下,我們可以非常方便的在每個過濾器中實現一個特定功能,從而創建結構復雜的應用程序。在應用程序中你可以構造任意多個過濾器,將它們串接起來完成任務。

            在SAX API中org.xml.sax.XMLFilter接口繼承了org.xml.sax.XMLReader接口。它與XMLReader不同的是它不像XMLReader那樣通過解析文檔來獲取事件,而是從其他XMLReader中獲取事件,當然這也包括從其他的XMLFilter中獲取事件。在org.xml.sax.XMLFilter中有兩個方法:

            方法名稱 方法描述
            Public void setParent(XMLReader parent) 設置父XMLReader。這個方法讓應用程序將這個過濾器連接到它的父XMLReader (也可能是另一個過濾器)。
            Public XMLReader getParent() 獲取父XMLReader。這個方法讓應用程序可以查詢父XMLReader(也可能是另一個過濾器)。最好不要在父XMLReader中直接進行任何操作:讓所有的事件通過這個過濾器來處理。

            我們不需要自己實現org.xml.sax.XMLFilter接口,在SAX API 中提供了一個org.xml.sax.helper.XMLFilterImpl類,它不僅實現了org.xml.sax.XMLFilter接口而且還實現了其他四個核心處理器接口,我們只需要繼承它即可完成我們的過濾器。剛開始使用XMLFilterImpl比較容易讓人迷惑,你只需要記?。?/p>

            1. 在你繼承的XMLFilterImpl類中用set****()方法這冊的事件處理器是給過濾后的事件流而用的。
            2. 在你繼承的XMLFilterImpl類中實現的那些事件處理方法,比如startDocument()、startElement()、characters()等才是這個過濾器實現它自身功能的地方。而通過繼承XMLFilterImpl而實現的這個類會被造型成各種處理器(它本身實現了四個處理器接口)用在它的父XMLReader中。這個步驟會在你調用自己創建的過濾器的parse()方法開始解析文檔時被自動執行(請參見SAX源代碼)。
            3. 如果不是使用帶參數的構造器創建XMLFilter對象,務必使用setParent(XMLReader parent)方法連接它的父XMLReader。
            4. 如果使用多個過濾器的話,執行順序是從父親到最后的過濾器。但是開始解析卻要調用最后一個過濾器的parse()方法。

            下面讓我們結合已有的例子來演示過濾器org.xml.sax.XMLFilter的作用。我們在這個過濾器中要過濾掉<技術書籍>這個元素,最后得到的事件流還是由上邊實現的MyDefaultHandler來處理。源代碼如下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);
                        }
                        /**
                        * 過濾掉元素<技術書籍>的開始事件
                        **/
                        public void startElement( String namespaceURI, String localName,
                        String fullName, Attributes attributes )
                        throws SAXException {
                        currentElement = localName;
                        if ( !localName.equals("技術書籍") ) {
                        super.startElement(namespaceURI, localName, fullName, attributes);
                        }
                        }
                        /**
                        * 過濾掉元素<技術書籍>的結束事件
                        **/
                        public void endElement(String namespaceURI, String localName, String
                        fullName)
                        throws SAXException {
                        if ( !localName.equals("技術書籍") ) {
                        super.endElement(namespaceURI, localName, fullName);
                        }
                        }
                        /**
                        * 過濾掉元素<技術書籍>中的內容
                        **/
                        public void characters(char[] buffer, int start, int length)
                        throws SAXException {
                        if ( !currentElement.equals("技術書籍") ) {
                        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") ;
                        //初始化過濾器
                        XMLFilter myFilter=new MyFilter(reader);
                        // 創建DefaultHandler的實例
                        DefaultHandler defaultHandler=new MyDefaultHandler();
                        //為過濾后的事件流設置ContentHandler
                        myFilter.setContentHandler(defaultHandler);
                        //為過濾后的事件流設置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());
                        }
                        }
                        }

            這里是最后的執行結果,我們可以發現有關<技術書籍>的全部事件已經被過濾掉了。認真看一下結果,你一定覺得奇怪,為什么<技術書籍>元素的孩子元素仍然存在。請記住SAX是把XML文檔解析成事件流,所有沒有被過濾的事件都會保留下來。這就是SAX和DOM的最大不同。在DOM中文檔被解析成了樹狀模型,如果你刪除一個元素,那么這個元素以及它的孩子元素就都會被刪除,這符合樹狀模型的特點。

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

            *******開始解析文檔*******
                        元素: [我的書架] 開始解析!
                        元素: [圖書] 開始解析!
                        元素: [書名] 開始解析!
                        內容是: JAVA 2編程詳解
                        元素: [書名] 解析結束!
                        元素: [價格] 開始解析!
                        屬性名稱:貨幣單位 屬性值:人民幣
                        內容是: 150
                        元素: [價格] 解析結束!
                        元素: [購買日期] 開始解析!
                        內容是: 2000,1,24
                        元素: [購買日期] 解析結束!
                        元素: [圖書] 解析結束!
                        前綴映射: book 開始!  它的URI是:http://javausr.com
                        元素: [book:文學書籍] 開始解析!
                        元素: [book:文學書籍] 解析結束!
                        前綴映射: book 結束!
                        元素: [歷史書籍] 開始解析!
                        元素: [歷史書籍] 解析結束!
                        元素: [我的書架] 解析結束!
                        *******解析文檔結束*******





            回頁首


            一些值得注意的問題

            首先是有關元素內容的問題,在SAX API定義中元素內容可以在一次事件(由characters()方法處理)中返回,也可以在多次事件中返回,這樣我們就應該考慮不能一次得到所有內容數據的情況。一般的解決辦法是定義一個StringBuffer由它來保存內容數據,在元素結束或者新元素開始的時候清空這個StringBuffer從而可以保存新的內容數據。請參考上面的相應的源代碼。

            還有在SAX API中特意提到從 characters(char[] ch,int start,int length)方法中提取數據時一定不要從返回的字符數組范圍之外讀取,這一點我們也要切記。

            另一個值得注意的問題是,在 startElement()方法中返回的Attributes屬性列表中的屬性順序并沒有被特意規定,在不同的SAX實現中也各不相同。所以我們在編寫程序時不要把屬性順序想成一定的。





            回頁首


            SAX與DOM的比較

            通過上面的介紹我想大家對SAX已經有了一個基本的了解。每一個進行XML開發的編程人員都知道DOM,那為什么在有了DOM這個功能強大的文檔對象模型之后,我們還需要SAX?這就要從它們根本不同的實現方法上來分析。DOM解析器是通過將XML文檔解析成樹狀模型并將其放入內存來完成解析工作的,而后對文檔的操作都是在這個樹狀模型上完成的。這個在內存中的文檔樹將是文檔實際大小的幾倍。這樣做的好處是結構清除、操作方便,而帶來的麻煩就是極其耗費系統資源。而SAX正好克服了DOM的缺點。SAX解析器的處理過程是通讀整個文檔,根據文檔內容產生事件,而把對這些事件的處理交由事件處理器處理。SAX不需要在內存中保存整個文檔,它對系統資源的節省是顯而易見的。這樣在一些需要處理大型XML文檔和性能要求比較高的場合就要用SAX了。

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

            SAX DOM
            順序讀入文檔并產生相應事件,可以處理任何大小的XML文檔 在內存中創建文檔樹,不適于處理大型XML文檔。
            只能對文檔按順序解析一遍,不支持對文檔的隨意訪問。 可以隨意訪問文檔樹的任何部分,沒有次數限制。
            只能讀取XML文檔內容,而不能修改 可以隨意修改文檔樹,從而修改XML文檔。
            開發上比較復雜,需要自己來實現事件處理器。 易于理解,易于開發。
            對開發人員而言更靈活,可以用SAX創建自己的XML對象模型。 已經在DOM基礎之上創建好了文檔樹。

            通過對SAX和DOM的分析,它們各有自己的不同應用領域:

            1. SAX適于處理下面的問題:
            2. 對大型文檔進行處理。
            3. 只需要文檔的部分內容,或者只需要從文檔中得到特定信息。
            4. 想創建自己的對象模型的時候。

            DOM適于處理下面的問題:

            1. 需要對文檔進行修改
            2. 需要隨機對文檔進行訪問,例如XSLT解析器。

            對SAX的介紹到這里就告一段落了,希望能對大家有所幫助:),本文的絕大部分參考資料都來源于http://www.megginson.com/SAX/ 以及SAX API(雖然說SAX有了自己新的網站http://sax.sourceforge.net/ 但我從來沒有成功訪問過!) ,感謝David Megginson和其他SAX開發人員給我們提供了這么一個好東東。本文如有錯誤和不妥的地方還請大家指正。





            回頁首


            附錄: SAX的版權聲明

            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



            參考資料

            學習
            • developerWorks 中國網站 XML 專區:在 developerWorks XML 專區可以得到更多技術文章、技巧、教程、標準以及 IBM 紅皮書。

            • IBM XML 認證:了解如何才能成為一名 IBM 認證的 XML 及相關技術的開發人員。


            獲得產品和技術
            • IBM 試用版軟件:使用 IBM 試用版軟件構建您的下一個開發項目,這些試用版軟件可直接從 developerWorks 下載獲得。


            討論


            關于作者

            王曉強,萬千程序開發者中的一員,并樂在其中。熱愛java和linux,一直利用java和xml相關技術進行應用開發,并在這些方面積累了豐富經驗??赏ㄟ^grekiller@yeah.net與他聯系。


            posted on 2010-04-23 09:07 baby-fly 閱讀(930) 評論(0)  編輯 收藏 引用 所屬分類: Java
            久久亚洲AV成人无码国产| 久久一区二区三区免费| 久久精品视屏| 久久综合鬼色88久久精品综合自在自线噜噜| 久久精品国产亚洲5555| 中文字幕久久精品| 国产精品久久一区二区三区| 精品一区二区久久久久久久网站| 久久久99精品成人片中文字幕| 狠狠色婷婷久久综合频道日韩| 国产精品欧美久久久天天影视| 伊人久久五月天| 久久最新精品国产| 久久99国产精品久久99| 狠狠色综合网站久久久久久久高清 | 久久99精品九九九久久婷婷| 久久99热这里只有精品66| 国产精品一久久香蕉产线看| 久久最新免费视频| 久久精品国产69国产精品亚洲| 伊人久久精品无码av一区| 久久久久女教师免费一区| 国产一级做a爰片久久毛片| 亚洲av成人无码久久精品| 午夜精品久久久久9999高清| 国内精品久久久久影院免费| 久久精品国产久精国产思思| 国内精品伊人久久久久777| 一级女性全黄久久生活片免费 | 久久综合伊人77777| 日韩精品国产自在久久现线拍| 久久不见久久见免费视频7| 伊人久久大香线蕉AV色婷婷色| 少妇被又大又粗又爽毛片久久黑人| 国产69精品久久久久9999| 99久久精品免费国产大片| 久久国产精品99精品国产987| AV狠狠色丁香婷婷综合久久| 国内精品久久久久久久久电影网| 国产精品一区二区久久精品涩爱 | 精品久久久久中文字|