[轉]使用 SAX 處理 XML 文件 和與DOM的區別

sdvingo發表於2011-08-05

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解析器,你可以從 得到它。讓我們下載得到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的設計實現與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();其中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對名稱空間的支援,我在這裡特意加了一個有名稱空間的元素,在這裡會產生相應的字首對映開始和結束事件。



    
        
            JAVA 2程式設計詳解書名>
            150價格>
            2000,1,24購買日期>
        圖書>      
    技術書籍>
    這裡的例子程式只是簡單地將遇到的事件資訊列印出來。我們首先實現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:saxclasses>java com.javausr.saxexample.MySAXApp d:book.xml
*******開始解析文件*******
元素: [我的書架] 開始解析!
元素: [技術書籍] 開始解析!
元素: [圖書] 開始解析!
元素: [書名] 開始解析!
        內容是: JAVA 2程式設計詳解
元素: [書名] 解析結束!
元素: [價格] 開始解析!
        屬性名稱:貨幣單位 屬性值:人民幣
        內容是: 150
元素: [價格] 解析結束!
元素: [購買日期] 開始解析!
        內容是: 2000,1,24
元素: [購買日期] 解析結束!
元素: [圖書] 解析結束!
元素: [技術書籍] 解析結束!
字首對映: book 開始!  它的URI是:
元素: [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("*****************************");
    }
}
[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/443058/viewspace-1053476/,如需轉載,請註明出處,否則將追究法律責任。

[轉]使用 SAX 處理 XML 文件 和與DOM的區別
請登入後發表評論 登入
全部評論

相關文章