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

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

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

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    就目前來說,有三種方式可以解析XML文件:DOMSAXStAXDOM將整個XML文件加載到內(nèi)存中,并構(gòu)建出節(jié)點樹;應(yīng)用程序可以通過遍歷節(jié)點樹的方式來解析XML文件中的各個節(jié)點、屬性等信息;這種方式便于對XML節(jié)點的添加修改等,而且解析也很方便,然后它比較耗費內(nèi)存,解析速度也不快。SAX則是基于事件的解析,解析器在一次讀取XML文件中根據(jù)讀取的數(shù)據(jù)產(chǎn)生相應(yīng)的事件,由應(yīng)用程序?qū)崿F(xiàn)相應(yīng)的事件處理邏輯,即它是一種“推”的解析方式;這種解析方法速度快、占用內(nèi)存少,但是它需要應(yīng)用程序自己處理解析器的狀態(tài),實現(xiàn)起來會比較麻煩,而且它只支持對XML文件的讀取,不支持寫入。不同于SAX的“推”的解析方式,StAX是基于“拉”的解析方式,即應(yīng)用程序根據(jù)自己的需要控制解析器的讀取;這種方式繼承了SAX解析速度快、占用內(nèi)存少等優(yōu)點,同時它好保持了接口簡單、編程容易等特點;不過它也應(yīng)該不支持寫XML文件的,木有仔細(xì)看過這個框架,先猜測一下~~。貌似DOM底層采用了SAX的實現(xiàn),因而本文首先介紹基于SAX方式的XML文件解析。

    SAX的解析框架相對比較簡單,以下是它核心類關(guān)系圖:


    InputSource

    InputSourceSAX中對要被解析的XML資源文件的抽象,它封裝了以下信息:

    private Reader characterStream;

    private InputStream byteStream;

    private String systemId;

    private String publicId;

    private String encoding;

    應(yīng)用程序可以顯示的設(shè)置characterStreambyteStream來指定實際的XML資源文件,或者使用systemIdpublicId的方式來定位XML資源文件。這里systemIdpublicId借用了導(dǎo)入DTD文件是定義的SYSTEMPUBLIC概念。在DTD中,SYSTEMPUBLIC都表示外部資源文件,所不同的是SYSTEM指定的是具體的資源文件,它可以是相對路徑也可以是絕對路徑,而PUBLIC則是使用定義的名稱查找資源文件。如以下是Spring使用DTD時的寫法:

    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd ">  

    這里的publicId是:“-//SPRING//DTD BEAN//EN

    systemId是:“http://www.springframework.org/dtd/spring-beans.dtd

    很多框架都實現(xiàn)了自己的EntityResolver,以實現(xiàn)自定義的Entity查找邏輯,就像Spring,它首先在當(dāng)前ClassPath下查找對應(yīng)的DTD文件(Spring.jar文件中)。

    在實現(xiàn)中,很少去使用publicId的,以Spring源碼中沒有使用publicId,甚至我在xerces源碼中都沒有看到對publicId的處理,不過看它的文檔,貌似是提供了publicId的實現(xiàn),不知道是Java的實現(xiàn)版本沒有提供還是我沒有找對地方,而且按測試的結(jié)果,它不能只存在publicId也就是說如果存在publicId的話,systemId也必須存在。按著文檔對publicId做一個簡單的解釋,在以上的定義中“-//SPRINT/DTD BEAN//EN”只是一個名字,在XML解析引擎中使用這個名字去查找資源文件真正的位置,這個名字和資源文件實際路徑的映射文件一般定義在一個catalog文件中(有些引擎支持配置),其定義格式一般為:

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">

        <public publicId="-//OASIS//DTD XML DocBook V4.1.2//EN"

            uri="docbook/xml/docbookx.dtd"/>

        <system systemId="urn:x-oasis:docbook-xml-v4.1.2"

            uri="docbook/xml/docbookx.dtd"/>

        <delegatePublic publicIdStartString="-//Example//"

              catalog="http://www.example.com/catalog"/>

        <public publidId="-//SPRING//DTD BEAN//EN"

             uri="http://www.springframework.org/dtd/spring-beans.dtd" />

    </catalog>

    publicId以目前來看感覺可以忽略。有興趣對這個做深入研究的童鞋可以參考一下文檔:

    http://xerces.apache.org/xml-commons/components/resolver/resolver-article.html

    http://supportweb.cs.bham.ac.uk/documentation/tutorials/docsystem/build/tutorials/docbooksys/segmentedhtml/ch08s02.html

    在使用InputSource時,還需要注意資源文件的查找順序問題,即InputSource中的characterStreambyteStreamsystemId都可以表示一個資源文件,因而需要定義它們的查找順序:即先查看characterStream字段,如果有值則使用(此時encoding字段無效);然后查看byteStream字段,如果有值,則使用;最后嘗試使用URI解析systemId字段(可以是相對路徑),如果能找到對應(yīng)的資源文件,則使用;否則,拋出異常。對后兩種情況,可以指定encoding字段表示資源文件的編碼方式。

     

    XMLReaderFactory

    XMLReaderFactory從這個類的名字中已經(jīng)能知道它是用于創(chuàng)建XMLReader實例的工場類,它提供兩個靜態(tài)方法以創(chuàng)建XMLReader實例:

    public static XMLReader createXMLReader();

    public static XMLReader createXMLReader(String className);

    對不帶參數(shù)的createXMLReader()方法,它實現(xiàn)了一種動態(tài)查找XMLReader具體實現(xiàn)類的方式:

    1.       首先查看系統(tǒng)屬性中是否存在“org.xml.sax.driver”屬性的定義,如果存在,則使用該屬性定義的XMLReader實現(xiàn)類。

    2.       其次查看ClassPath下是否存在“META-INF/services/org.xml.sax.driver”文件的定義,如果存在,則使用該文件中定義XMLReader的實現(xiàn)類。

    3.       否則,默認(rèn)使用“com.sun.org.apache.xerces.internal.parsers.SAXParser”類作為XMLReader的實現(xiàn)類。

    而對帶參數(shù)的createXMLReader()工場方法來說,它只是實例化傳入的XMLReader的實現(xiàn)類。

     

    XMLReader接口和實現(xiàn)

    XMLReader是實現(xiàn)真正解析XML資源文件的接口,之所以不使用Parser是因為這個名稱已經(jīng)在SAX1中被使用,而在SAX2中將實現(xiàn)解析的接口名稱重命名成XMLReader。在使用SAX解析XML資源文件時,默認(rèn)使用SAXParser實現(xiàn)類,它繼承自AbstractSAXParser(參考以上類關(guān)系圖)。XMLReader接口提供以下方法:

    public interface XMLReader {

        public boolean getFeature (String name)         throws SAXNotRecognizedException, SAXNotSupportedException;

        public void setFeature (String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException;

     

        public Object getProperty (String name) throws SAXNotRecognizedException, SAXNotSupportedException;

        public void setProperty (String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException;

     

        public void setEntityResolver (EntityResolver resolver);

        public EntityResolver getEntityResolver ();

        public void setDTDHandler (DTDHandler handler);

        public DTDHandler getDTDHandler ();

        public void setContentHandler (ContentHandler handler);

        public ContentHandler getContentHandler ();

        public void setErrorHandler (ErrorHandler handler);

        public ErrorHandler getErrorHandler ();

     

        public void parse (InputSource input)    throws IOException, SAXException;

        public void parse (String systemId) throws IOException, SAXException;

    }

    這個接口定義了一些操作XML解析器的屬性和方法:

    1.       Feature

    Feature值一般是一個URI的全稱,用于定義當(dāng)前解析器支持的特性,比如按注釋,所有解析器都要識別的特性有:

    http://xml.org/sax/features/namespaces

    http://xml.org/sax/features/namespace-prefixes

    還有其他一些常見的Feature有(默認(rèn)AbstractSAXParser支持的Feature):

    http://xml.org/sax/features/string-interning

    http://xml.org/sax/features/is-standalone

    http://xml.org/sax/features/xml-1.1

    http://xml.org/sax/features/lexical-handler/parameter-entities

    http://xml.org/sax/features/resolve-dtd-uris

    http://xml.org/sax/features/xmlns-uris

    http://xml.org/sax/features/unicode-normalization-checking

    http://xml.org/sax/features/use-entity-resolver2

    http://xml.org/sax/features/use-attributes2

    http://xml.org/sax/features/use-locator2

    http://xml.org/sax/features/internal/parser-settings

    http://xml.org/sax/features/internal/xinclude

    一些用戶自定義的解析器可以指定自己支持的FeatureXMLReader提供接口查詢、設(shè)置指定當(dāng)前XMLReader是否支持某種Feature

    2.       Property

    XMLReader還支持通過URI方式獲取解析器相關(guān)的一些屬性值,一些擴(kuò)展的事件Handler也可以通過該方式定義。如常見的屬性定義有:

    http://xml.org/sax/properties/document-xml-version

    http://xml.org/sax/properties/lexical-handler

    http://xml.org/sax/properties/declaration-handler

    http://xml.org/sax/properties/dom-node

    具體可以參考xerces中的介紹:

    http://xerces.apache.org/xerces2-j/properties.html

    3.       事件處理器(Event Handlers)注冊方法

    應(yīng)用程序通過注冊相應(yīng)的事件處理器來和XMLReader解析器教務(wù),解析器在解析過程中產(chǎn)生的事件都會通過調(diào)用相應(yīng)事件處理器中的相應(yīng)的方法來將信息傳給應(yīng)用程序。默認(rèn)支持的時間處理器有:

    EntityResolver:實現(xiàn)用戶自定義外部實體解析處理邏輯。

    DTDHandler:實現(xiàn)在NOTATION定義以及存在沒有解析的Entity定義時,用戶可以加入自定義的處理邏輯。

    ContentHandler:處理所有對XML內(nèi)容解析時產(chǎn)生的所有事件。

    ErrorHandler:解析器在解析過程中遇到錯誤時被調(diào)用。

    通過設(shè)置屬性值的方式,AbstractSAXParser還支持以下兩種Handler

    LexicalHandlerSAX2擴(kuò)展接口,提供更多的XML資源文件相關(guān)的信息,如注釋、CDATA等。

    DeclHandlerSAX2擴(kuò)展接口,在DTD定義事件中提供回調(diào)方法。

    4.       解析方法(parse

    解析器實現(xiàn)解析XML資源文件的方法。應(yīng)用程序可以傳入InputSource實例,也可以傳入systemId的值,即一個URI字符串或相對路徑指定的文件名。解析方法(parse)是線程同步的,對一個XMLReader實例在解析時,另一個線程會等待該線程結(jié)束后才開始解析新的文件,因而一般情況下,在多個線程中都會創(chuàng)建各自的XMLReader實例。然而當(dāng)一個XMLReader實例解析完成后,我們可以重用該XMLReader實例解析新的XML文件,此時之前的FeatureProperty以及Handler的注冊等信息都保持不變,我們可以手動調(diào)用相應(yīng)的方法改變之。

     

    EntityResolver接口

    EntityResolver接口提供應(yīng)用程序自定義實體解析的擴(kuò)展點,即應(yīng)用程序可以注冊自己的實體解析類以實現(xiàn)自己特定的邏輯,如在本地、數(shù)據(jù)庫、網(wǎng)絡(luò)中查找外部實體。比如Spring就實現(xiàn)了自己的BeansDtdResolver,對InputSource小節(jié)中定義的spring-beans.dtd,它會先查找BeanDtdResolver類所在的目錄(一般為jar包內(nèi))中存在的spring-beans.dtd,如果存在該文件,則會加載該文件作為DTD文件,否則,使用默認(rèn)的EntityResolver(即返回null,則XML引擎自己提供的解析器)。

    然而什么是實體(Entity)呢?如果使用DTD作為XML文件的定義模板,那么在引入DTD文件時,即使引入一個外部實體,其PUBLICSYSTEM定義分別對應(yīng)publicIdsystemId。實體的另一個使用地方是在DTD文件定義時可以定義命名實體,而該命名實體可以在XML文件中使用(貌似這個用途不怎么多~~)。比如我們可以定義如下DTD文件:

    <!ELEMENT website (name,copyright)>

    <!ELEMENT name (#PCDATA)>

    <!-- Parameter Entity-->

    <!ENTITY % copyrightElement "<!ELEMENT copyright (#PCDATA)>">

    %copyrightElement;

    <!--Normal Entity-->

    <!ENTITY name "cnblog">

    <!--External Entity-->

    <!ENTITY copyright SYSTEM "copyright.desc">

    DTD可以在以下XML文件中使用:

    <?xml version="1.1" encoding="UTF-8"?>

    <!DOCTYPE website SYSTEM "../dtds/entitiesDtD.dtd">

    <website>

        <name>&name;</name>

        <copyright>&copyright;</copyright>

    </website>

    此時,在解析XML文件時,&name;值為cnblog,而&copyright;的值為copyright.desc文件中的內(nèi)容。EntityResolver接口也只有在需要解析外部實體是才會被調(diào)用,比如在解析&name;實體時就不會被調(diào)用。

    EntityResolver的接口定義如下:

    public interface EntityResolver {

        public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;

    }

    resolveEntity()方法返回用戶自定義的InputSource實例,如果該方法返回null,則XML解析引擎默認(rèn)為外部實體為URL,并使用該URL創(chuàng)建InputSource

    一般情況下使用XML解析引擎內(nèi)部默認(rèn)的實現(xiàn)即可,但是像Spring這種從本地讀取DTD文件時,則需要實現(xiàn)自己的EntityResolver,其實現(xiàn)核心代碼如下:

    public InputSource resolveEntity(String publicId, String systemId) throws IOException {

        if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {

            ......

            Resource resource = new ClassPathResource(dtdFile, getClass());

            InputSource source = new InputSource(resource.getInputStream());

            source.setPublicId(publicId);

            source.setSystemId(systemId);

            return source;

    ......

        }

        // Use the default behavior -> download from website or wherever.

        return null;

    }

    如果使用XSD作為XML的定義模板,我們可以定義schemaLocationXSD文件引入,比如Spring配置文件中的用法:

    <beans xmlns="http://www.springframework.org/schema/beans"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xmlns:context="http://www.springframework.org/schema/context"

           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

       

        <bean id="myCustomer" class="levin.spring.test.Customer">

            <property name="name" value="customer"></property>

            <property name="address" value="shanghai"></property>

            <property name="user" ref="user"></property>

        </bean>

        <context:property-holder id="propertyHolder" class="org.springframework.context.PropertyHolder" />

    </beans>

    以上例子,我們將所有XML的命名空間定義在beans標(biāo)簽中,并在標(biāo)簽中使用xsi:schemaLocation定義了所有xsd文件的位置,在XML解析中,每當(dāng)遇到一個新的命名空間,解析器就會先調(diào)用resolveEntity方法,并將xsi:schemaLocation中定義的位置傳入resolveEntity()方法(publicIdnullsystemIdschema的位置,如http://www.springframework.org/schema/context/spring-context.xsd 默認(rèn)解析器會從這個位置上查找xsd文件,然后開始解析相應(yīng)的標(biāo)簽,比如對默認(rèn)命名空間所使用的xsd文件,SAXParser會先解析該文件,然后真正解析beans標(biāo)簽。如DTD作為外部實體的導(dǎo)入,應(yīng)用程序可以定義自己的EntityResolver以實現(xiàn)自定義的外部實體查找邏輯,如Spring定義自己的EntityResolverPluggableSchemaResolver),以從本地查找xsd文件:

    public InputSource resolveEntity(String publicId, String systemId) throws IOException {

    String resourceLocation = getSchemaMapping(systemId);

    if (resourceLocation != null) {

            Resource resource = new ClassPathResource(resourceLocation, this.classLoader);

            InputSource source = new InputSource(resource.getInputStream());

            source.setPublicId(publicId);

            source.setSystemId(systemId);

            return source;

    }

        // Use the default behavior -> download from website or wherever.

        return null;

    }

    其實以上配置文件還可以寫成:

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

        <bean id="myCustomer" class="levin.spring.test.Customer">

            <property name="name" value="customer"></property>

            <property name="address" value="shanghai"></property>

            <property name="user" ref="user"></property>

        </bean>

        <context:property-holder xmlns:context="http://www.springframework.org/schema/context"

            xsi:schemaLocation="http://www.springframework.org/schema/context

                             http://www.springframework.org/schema/context/spring-context.xsd"

            id="propertyHolder" class="org.springframework.context.PropertyHolder" />

    </beans>

    最后,默認(rèn)SAXParser默認(rèn)實現(xiàn)不支持XSD文件的解析,我們需要手動的設(shè)置以下屬性以打開這一屬性:

    XMLReader reader = XMLReaderFactory.createXMLReader();            reader.setProperty("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");            reader.setFeature("http://apache.org/xml/features/validation/schema", true);

    DTDHandler接口

    提供用戶在NOTATION定義以及存在沒有解析的Entity定義時注冊用戶自定義的處理邏輯。這里需要解釋兩個定義:什么是NOTATION以及什么是沒有解析的Entity定義。

    首先來解釋一下什么是NOTATION:在XML文件中可以包含一些非XML的數(shù)據(jù),NOTATION即定義了這種非XML數(shù)據(jù)的格式,從而應(yīng)用程序可以識別這些數(shù)據(jù)。然而什么是非XML數(shù)據(jù)呢?舉個例子,以下DTD定義了一個image標(biāo)簽,它包含source屬性,而這個source屬性指向某個gif圖片:

    <!NOTATION gif SYSTEM "image/gif">

    <!ENTITY JENN SYSTEM "http://images.about.com/sites/guidepics/html.gif" NDATA gif>

    <!ELEMENT image EMPTY>

    <!ATTLIST image source ENTITY #REQUIRED>

    對應(yīng)的XML數(shù)據(jù)段:

    <image source="JENN" />

    我們知道在XML文件文本數(shù)據(jù),中一般只能包含文本數(shù)據(jù),那么如何將圖片文件嵌入到XML文件中呢?XML提供了三種方法實現(xiàn)在XML文件中包含二進(jìn)制數(shù)據(jù):一種是使用以上NOTATION的方式;一種是使用mine擴(kuò)展;還有一種是將二進(jìn)制數(shù)據(jù)保存到<![CDATA[ …]]>中(具體可以參考:http://www.ibm.com/developerworks/cn/xml/x-binary/index.html)。

    其次,什么是沒有解析的Entity定義呢?沒有解析并不是在DTD文件中所有沒有被使用到的Entity,而是指所有那些NDATA類型的實體定義,因為這些實體需要用戶自己去解析,因而XML解析引擎不會默認(rèn)對他們做解析。比如以上的&name;實體是被解析過的,因而它會轉(zhuǎn)換成“cnblog”值顯示出來;而以上JENN的這個實體則沒有被XML解析引擎解析,因而它會觸發(fā)DTDHandler接口的調(diào)用。

    那么我們再來看一下DTDHandler接口提供的方法吧:

    public interface DTDHandler {

    public abstract void notationDecl (String name, String publicId,

    String systemId) throws SAXException;

    public abstract void unparsedEntityDecl (String name, String publicId,

    String systemId,  String notationName) throws SAXException;

    }

    這兩個方法正好提供了DTDHandler兩種特性的回調(diào)方法:

    1.       當(dāng)XML解析引擎在解析DTD文件中NOTATION的定義時,它會調(diào)用notationDecl()方法,此時用戶注冊的DTDHandler則可以根據(jù)傳入的publicIdsystemId解析當(dāng)前NOTATION相應(yīng)的數(shù)據(jù)。

    2.       當(dāng)XML解析引擎在解析DTD文件中NDATA類型的Entity定義時,它會調(diào)用unparsedEntityDecl()方法。用戶可以根據(jù)傳入的publicIdsystemIdnotationName等信息解析當(dāng)前Entity,比如以上則是從指定的URL中讀取html.gif文件,讀到的數(shù)據(jù)可以name作為key保存起來,之后,當(dāng)用戶在解析source屬性是,可以根據(jù)source屬性中的值得到相應(yīng)的數(shù)據(jù)保存起來。

    一般來說DTD文件的解析要在所有XML文件中的Element解析之前,因而DTDHandler中的回調(diào)函數(shù)一般在startDocument()事件之后,而在第一個startElement()事件之前。

    最后,感覺應(yīng)該很少有用戶會去使用NOTATION的概念吧,除非用戶想在XML文件中包含二進(jìn)制數(shù)據(jù),其實我個人感覺這種二進(jìn)制數(shù)據(jù)的實現(xiàn)方式也不好,所以個人感覺很少有人需要去實現(xiàn)DTDHandler的接口。

    ContentHandler接口

    ContentHandler接口處理所有對XML內(nèi)容解析時產(chǎn)生的所有事件。它的接口定義如下:

    public interface ContentHandler {

        public void setDocumentLocator(Locator locator);

        public void startDocument() throws SAXException;

        public void endDocument() throws SAXException;

    public void startPrefixMapping (String prefix, String uri)

        throws SAXException;

        public void endPrefixMapping (String prefix) throws SAXException;

    public void startElement (String uri, String localName, String qName,

    Attributes atts) throws SAXException;

        public void endElement (String uri, String localName, String qName)

            throws SAXException;

    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 (String target, String data)

            throws SAXException;

        public void skippedEntity (String name) throws SAXException;

    }

    a.       setDocumentLocator方法

    解析器解析XML文件內(nèi)容時,它會首先調(diào)用setDocumentLocator設(shè)置一個Locator實例,應(yīng)用程序可以從Locator實例中獲取解析器當(dāng)前在解析的位置:

    public interface Locator {

        public abstract String getPublicId();

        public abstract String getSystemId();

     

        public abstract int getLineNumber();

        public abstract int getColumnNumber();

    }

    其中publicIdsystemId用于定位正在解析的文件,它可以是正在解析的XML文件,也可以是在XML中引入的外部實體文件。lineNumbercolumnNumber則用于定位當(dāng)前事件發(fā)生時所在解析文件的行號和列號,行號和列號只是用于近似的診斷信息,不保證完全正確。

    b.      startDocumentendDocument方法

    這兩個方法提供在XML文件解析開始和結(jié)束的位置插入應(yīng)用程序自定義的邏輯,只是兩個常見的擴(kuò)展點。

    c.       startPrefixMappingendPrefixMapping方法

    這兩個方法是在引入一個命名空間和結(jié)束該命名空間的作用域時調(diào)用的,比如在EntityResolver接口中定義了兩種方式的Spring XML配置文件,對第一個定義文件中,context命名空間在beans標(biāo)簽中定義,因而在解析beans標(biāo)簽時,它會首先調(diào)用startPrefixMapping方法,其參數(shù)中prefixcontexturihttp://www.springframework.org/schema/context,然后在調(diào)用beans標(biāo)簽的startElement方法;此時context命名空間的作用域范圍在beans標(biāo)簽內(nèi),因而當(dāng)beans結(jié)束后(在調(diào)用beans標(biāo)簽相關(guān)的endElement方法),會調(diào)用endPrefixMapping方法。而在第二個定義文件中,context命名空間在context:property-holder標(biāo)簽中定義,此時startPrefixMapping方法會在解析該標(biāo)簽時調(diào)用,但是在調(diào)用該標(biāo)簽相關(guān)的startElement之前;并且此時context的命名空間的作用域也只是在context:property-holder中。

    d.      startElementendElement方法

    在解析一個標(biāo)簽開始和結(jié)束時分別會調(diào)用這兩個方法。在調(diào)用startElement之前,xsd外部實體的引入、解析、驗證都已經(jīng)完成了(resolveEntity方法),命名空間解析也已經(jīng)完成(startPrefixMapping方法)。在startElement方法的參數(shù)中,uri為該標(biāo)簽命名空間所的uri的值,如http://www.springframework.org/schema/context,如果沒有定義則為空;localName指本地名字,如property-holderqName為包含命名空間的名字,如contextproperty-holderatts為該標(biāo)簽中定義的屬性值,它封裝在Attributes接口中:

    public interface Attributes {

    public abstract int getLength ();

     

        public abstract String getURI (int index);

        public abstract String getLocalName (int index);

        public abstract String getQName (int index);

        public abstract String getType (int index);

        public abstract String getValue (int index);

     

        public int getIndex (String uri, String localName);

        public int getIndex (String qName);

        public abstract String getType (String uri, String localName);

        public abstract String getType (String qName);

        public abstract String getValue (String uri, String localName);

        public abstract String getValue (String qName);

    }

    Attributes類提供了兩種類型的查找方式:索引和命名屬性。其中索引的順序并沒有定義,因而在不同版本中的順序可能是不同的,這里沒用定義遍歷所有屬性的方法,因而我們可以使用getLength方法獲取屬性個數(shù),然后使用索引方式獲取相應(yīng)的值,而在其他情況下,個人不能推薦使用索引方式。對命名屬性,可以使用兩種方式:一種是使用包含命名空間的全名(qName),另一種是使用urilocalName的方式定義。

    關(guān)于getType,這里的type指的是DTDXSD中定義的屬性的類型:"CDATA", "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY", "ENTITIES", or "NOTATION",默認(rèn)為CDATA。而在Java讀取到的所有屬性的值都是字符串。

    e.       characters方法

    SAXParser在沒讀到一些非標(biāo)簽內(nèi)中的字符串時,都會調(diào)用該方法,這些字符串可以是某個標(biāo)簽的值,也可以是標(biāo)簽之前的空格和換行符。如一下XML數(shù)據(jù):


    會在標(biāo)記的7個位置分別調(diào)用characters方法,其中ch數(shù)組是文件讀取的字符串?dāng)?shù)組,其大小默認(rèn)為2k,可以使用http://apache.org/xml/properties/input-buffer-size屬性值設(shè)置其大小,但是如果新設(shè)置的大小不能一次讀取不可分割的數(shù)據(jù)內(nèi)容時,該大小或增加;start指定當(dāng)前字符串在ch數(shù)組中的起始位置;length則是指當(dāng)前字符串的長度。

    如果我們需要讀取該部分的字符串內(nèi)容,可以使用以下方式:

    new String(ch, start, length)

    f.        ignorableWhitespace方法

    當(dāng)解析器遇到可忽略的空格時被調(diào)用。什么是可忽略的空格呢?可以參考:http://www.w3.org/TR/2006/REC-xml-20060816/#sec-white-space,我一直沒有讀懂文檔中的解釋.....而且據(jù)xerces中解釋,ignorableWhitespace只用于DTD中,可參考:http://xerces.apache.org/xerces2-j/faq-sax.html中相關(guān)內(nèi)容。不過貌似我們很少使用這個方式,我就不深究了。

    g.       processingInstruction方法

    XML文件中還可以定義一下指令,以供一些特定的處理工具讀取并處理。SAXPaser在遇到這些指令時,就會調(diào)用這個方法。這些指令如:

    <?xml-stylesheet href="show.css" type="text/css" ?>

    此時target為:xml-stylesheetdata為:href=”show.css” type=”text/css”

    h.      skippedEntity方法

    當(dāng)SAXParser跳過一個實體時,該方法會被調(diào)用。那么什么情況下它會跳過一個實體的解析呢?這個我貌似也一直木有讀懂….幸好這個方法用的也不多。

    ErrorHandler接口

    ErrorHandler在解析器在解析過程中遇到錯誤時被調(diào)用。ErrorHandler分成三個級別:warnerrorfatalerror,解析器會根據(jù)當(dāng)前錯誤的級別調(diào)用相應(yīng)的方法。

    public interface ErrorHandler {

        public abstract void warning (SAXParseException exception)

        throws SAXException;

        public abstract void error (SAXParseException exception)

        throws SAXException;

        public abstract void fatalError (SAXParseException exception)

        throws SAXException;

    }

    DefaultHandler

    DefaultHandler類實現(xiàn)了所有以上接口EntityResolverDTDHandlerContentHandlerErrorHandler,并提供了所有方法的空實現(xiàn)。我們在使用SAX時,一般都是繼承自DefaultHandler,然后實現(xiàn)需要的方法,而保持其他方法默認(rèn)實現(xiàn)。

    LexicalHandler接口

    該接口是SAX2提供的擴(kuò)展接口,它可以處理XML資源文件中更多的詞匯(Lexical)信息,如注釋、CDATA等。可以使用以下方法注冊該接口:

    xmlReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);

    LexicalHandler接口定義:

    public interface LexicalHandler {

        //在一個DTD定義開始時調(diào)用

        public abstract void startDTD (String name, String publicId,                String systemId)

        throws SAXException;

    //在一個DTD定義結(jié)束時調(diào)用

        public abstract void endDTD ()

        throws SAXException;

        //在一個實體解析開始時調(diào)用

        public abstract void startEntity (String name)

        throws SAXException;

        //在一個實體解析結(jié)束時調(diào)用

        public abstract void endEntity (String name)

        throws SAXException;

        //CDATA數(shù)據(jù)區(qū)開始時調(diào)用

        public abstract void startCDATA ()

        throws SAXException;

        //在一個CDATA數(shù)據(jù)區(qū)結(jié)束后調(diào)用

        public abstract void endCDATA ()

        throws SAXException;

        //所有的注釋信息,包括外部實體(如DTD)中的注釋信息

        public abstract void comment (char ch[], int start, int length)

        throws SAXException;

    }

    DeclHandler接口

    SAX2擴(kuò)展接口,它提供了DTD定義事件的回調(diào)方法。可以使用一下方法注冊該接口:

    xmlReader.setProperty("http://xml.org/sax/properties/declaration-handler", handler);

    DeclHandler接口定義:

    public interface DeclHandler {

        //定義一個Element標(biāo)簽時的回調(diào),nameElement Namemodel”EMPTY”, “ANY”,

        //或者所有其他復(fù)雜Element定義的值

        public abstract void elementDecl (String name, String model)

        throws SAXException;

    //定義一個屬性標(biāo)簽時的回調(diào)。

    public abstract void attributeDecl (String eName, String aName,

            String type, String mode, String value)

        throws SAXException;

        //定義一個內(nèi)部實體時的回調(diào)

        public abstract void internalEntityDecl (String name, String value)

        throws SAXException;

        //定義一個外部實體時的回調(diào)

        public abstract void externalEntityDecl (String name, String publicId,

                             String systemId)

        throws SAXException;

    }

     

    一個具體SAX解析實例

    寫了那么多,花了我好幾天的時間,不過還是收獲不少,至少對SAX解析XML的方式有了一個比較深入的了解了。最后使用一個簡單的例子結(jié)束這篇文章吧。

    首先簡單的定義XML文件:

    <?xml version="1.0" encoding="UTF-8"?> 

    <books> 

        <book id="12"> 

            <name>thinking in java</name> 

            <price>85.5</price> 

        </book> 

        <book id="15"> 

            <name>Spring in Action</name> 

            <price>39.0</price> 

        </book> 

    </books>

    對應(yīng)Book類:

    class Book {

        private String id;

        private String name;

    private double price;

    }

    一個簡單的解析器:

    public class BookXmlParser extends DefaultHandler {

        private Locator locator;

       

        private List<Book> books;

        private Book currentBook;

        private String preTag;

       

        @Override

        public void setDocumentLocator(Locator locator) {

            this.locator = locator;

        }

       

        @Override

        public void startDocument() throws SAXException {

            books = new ArrayList<Book>();

            currentBook = null;

            preTag = null;

        }

     

        @Override

        public void startElement(String uri, String localName, String qName,

                Attributes attributes) throws SAXException {

            if("book".equals(qName)) {

                currentBook = new Book();

                currentBook.setId(attributes.getValue("id"));

            }

            preTag = qName;

        }

     

        public void endElement(String uri, String localName, String qName)

                throws SAXException {

            if("book".equals(qName)) {

                books.add(currentBook);

                currentBook = null;

            }

            preTag = null;

        }

     

        public void characters(char ch[], int start, int length)

                throws SAXException {

            if(preTag != null && currentBook != null) {

                String value = new String(ch, start, length);

                if("name".equals(preTag)) {

                    currentBook.setName(value);

                } else if("price".equals(preTag)) {

                    currentBook.setPrice(Double.parseDouble(value));

                }

            }

        }

     

        public void warning(SAXParseException e) throws SAXException {

            System.out.println("Warning occurred: \n");

            System.out.println("Location Info: " + locatorInfo());

            e.printStackTrace(System.out);

        }

     

        public void error(SAXParseException e) throws SAXException {

            System.out.println("Error occurred: \n");

            System.out.println("Location Info: " + locatorInfo());

            e.printStackTrace(System.out);

        }

     

        public void fatalError(SAXParseException e) throws SAXException {

            System.out.println("Fatal Error occurred: \n");

            System.out.println("Location Info: " + locatorInfo());

            e.printStackTrace(System.out);

            throw e;

        }

       

        private String locatorInfo() {

            return "resource: " + locator.getSystemId() + ", Locator Info: [" +

                    locator.getLineNumber() + ", " + locator.getColumnNumber() + "]";

        }

     

        public static void main(String[] args) throws Exception {

            XMLReader xmlReader = XMLReaderFactory.createXMLReader();

            BookXmlParser handler = new BookXmlParser();

            xmlReader.setContentHandler(handler);

            xmlReader.setEntityResolver(handler);

            xmlReader.setErrorHandler(handler);

            xmlReader.setDTDHandler(handler);

            xmlReader.parse("resources/xmlfiles/book.xml");

            System.out.println("Book List:");

            System.out.println(handler.books);

        }

    }

    posted on 2012-11-18 20:10 DLevin 閱讀(15011) 評論(0)  編輯  收藏 所屬分類: Core Java
    主站蜘蛛池模板: 亚洲熟妇av一区二区三区| 在线精品亚洲一区二区三区| 亚洲欧洲国产综合| 日韩人妻一区二区三区免费| 久久91亚洲精品中文字幕| 日韩免费电影网址| 亚洲一区二区三区日本久久九| 免费观看久久精彩视频| 精品亚洲综合在线第一区| 亚洲免费人成在线视频观看| 亚洲三级电影网站| 亚洲免费在线视频观看| 亚洲喷奶水中文字幕电影| 成人影片麻豆国产影片免费观看 | 亚洲国产精品日韩av不卡在线| 97人伦色伦成人免费视频| 亚洲第一成年免费网站| 免费一看一级毛片人| 一本久久A久久免费精品不卡| 亚洲第一页综合图片自拍| 国产在线观看无码免费视频| 亚洲avav天堂av在线不卡| 免费观看激色视频网站bd | 亚洲国产av一区二区三区丶| 毛片免费观看视频| 男男黄GAY片免费网站WWW| 亚洲伊人久久大香线蕉综合图片| 99re6在线精品视频免费播放| 亚洲偷自精品三十六区| 亚洲av高清在线观看一区二区| 国产中文字幕在线免费观看 | 亚洲风情亚Aⅴ在线发布| 久久亚洲av无码精品浪潮| 99爱在线观看免费完整版| 国产AV旡码专区亚洲AV苍井空| 免费v片视频在线观看视频| 无码av免费一区二区三区试看| 在线观看亚洲AV日韩A∨| 伊人婷婷综合缴情亚洲五月| 国产精彩免费视频| 一级视频在线免费观看|