posts - 20,  comments - 7,  trackbacks - 0

          ?

          J2EE/XML 開(kāi)發(fā)者通常都是使用文檔對(duì)象模型 (DOM)API 或簡(jiǎn)單的 API for XML(SAX) API 來(lái)分析 XML 文檔。然而,這些 API 都有其缺點(diǎn)。其中, DOM API 的缺點(diǎn)之一是消耗大量的內(nèi)存,因?yàn)樵谠?/span> XML 文檔可以被導(dǎo)航之前,必須創(chuàng)建一個(gè)完整的 XML 文檔的內(nèi)存結(jié)構(gòu)。而 SAX API 的缺點(diǎn)在于,它實(shí)例了一種推分析模型 API ,其中分析事件是由分析器生成的。比較之下, StAX 則是基于一種拉分析模型。在本文中,你將首先創(chuàng)建你自己的 XML 文檔,然后學(xué)習(xí)使用各種不同方法來(lái)對(duì)之進(jìn)行分析;最后,我們使用事件生成的 StAX 拉方法。

          一、 Push 推分析之于拉分析 Pull

          ?



            比較于推分析,拉分析具有如下一些優(yōu)點(diǎn):

             1. 在拉分析中,事件是由分析應(yīng)用程序生成的,因此把分析規(guī)則提供到客戶(hù)端而不是分析器。

             2. 拉分析的代碼更簡(jiǎn)單并且它比推分析有更少的庫(kù)。

             3. 拉分析客戶(hù)端能同時(shí)讀多個(gè) XML 文檔。

             4. 拉分析允許你過(guò)濾 XML 文檔并且跳過(guò)分析事件。

          二、了解 StAX


          針對(duì)于 XML 的流式 API(StAX) ,是在 2004 3 月的 JSR 173 規(guī)范中引入,這是一種針對(duì) XML 的流式拉分析 API StAX JDK 6.0 提供的一種新特征,你可以從此處下載它的測(cè)試版本試用。

          一個(gè)推模型分析器不斷地生成事件,直到 XML 文檔被完全分析結(jié)束。但是,拉分析由應(yīng)用程序進(jìn)行調(diào)整;因此,分析事件是由應(yīng)用程序生成的。這意味著,使用 StaX ,你可以推遲分析 - 在分析時(shí)跳過(guò)元素并且分析多個(gè)文檔。在使用 DOM API 的時(shí)候,你必須把整個(gè)的 XML 文檔分析成一棵 DOM 結(jié)構(gòu),這樣也就降低了分析效率。而借助于 StAX ,在分析 XML 文檔時(shí)生成分析事件。有關(guān)于 StAX 分析器與其它分析器的比較在此不多介紹。

          StAX API
          的實(shí)現(xiàn)是使用了 Java Web 服務(wù)開(kāi)發(fā)( JWSDP 1.6 ,并結(jié)合了 Sun Java 流式 XML 分析器 (SJSXP)- 它位于 javax.xml.stream 包中。 XMLStreamReader 接口用于分析一個(gè) XML 文檔,而 XMLStreamWriter 接口用于生成一個(gè) XML 文檔。 XMLEventReader 負(fù)責(zé)使用一個(gè)對(duì)象事件迭代子分析 XML 事件 - 這與 XMLStreamReader 所使用的光標(biāo)機(jī)制形成對(duì)照。本教程將基于 JDK 6.0 中的 StAX 實(shí)現(xiàn)來(lái)完成對(duì)一個(gè) XML 文檔的分析。

          其實(shí), StaX 僅僅是 JDK 6.0 所提供的 XML 新特征之一。新的 JDK 6.0 還提供了對(duì)針對(duì)于 XML-Web 服務(wù)的 Java 架構(gòu)( JAX-WS 2.0 ,針對(duì)于 XML 綁定的 Java API(JAXB) 2.0 XML 數(shù)字簽名 API 的支持,甚至還支持 SQL:2003 'XML' 數(shù)據(jù)類(lèi)型。

          三、安裝

          ?

            如果你正在使用 JDK 6.0 ,那么默認(rèn)情況下, StAX API 位于 Classpath 中。如果你在使用 JWSDP 1.6 ,請(qǐng)把 JWSDP 1.6 StAX API 添加到 classpath 中。

          ?

          jsr173_api.jar sjsxp.jar 添加到 CLASSPATH 變量中。在 <jwsdp-1.6> 目錄下安裝 JWSDP 1.6 Jsr173_api.jar 相應(yīng)于 JSR-173 API JAR Sjsxp.jar 相應(yīng)于 SJXSP 實(shí)現(xiàn) JAR

          ?

          四、使用 XMLStreamWriter 進(jìn)行寫(xiě)操作

          ?

          ?

            首先,你要?jiǎng)?chuàng)建將待分析的 XML 文檔。由 StAX XMLStreamWriter 生成 XML 。然而, XMLStreamWriter 的一個(gè)限制是,它不一定會(huì)生成良構(gòu)的文檔 - 而且生成的文檔也不一定是有效的。你需要確保生成的 XML 文檔是良構(gòu)的。列表 1 是一個(gè)由 XMLStreamWriter 生成的原始 XML 文檔的示例。

            在此,你試圖使用 XMLStreamWriter API 生成列表 1 中的 catalog.xml 。在本節(jié)中的代碼片斷節(jié)選自 XMLWriter.java 應(yīng)用程序,顯示于列表 2 中。首先,你將導(dǎo)入 StAX 包類(lèi),請(qǐng)參考下列編碼:

          import javax.xml.stream.*;
          import javax.xml.stream.events.*;
          import javax.xml.stream.XMLOutputFactory;
            

          // 首先你必須創(chuàng)建一個(gè)新的 XMLOutputFactory
          XMLOutputFactory outputFactory=XMLOutputFactory.newInstance();


          //
          接下來(lái),創(chuàng)建一個(gè) FileWriter 以輸出 XML 文檔 - 它將被生成到一個(gè) XML 文件中:
          FileWriter output=new FileWriter(new File("C:/STAX/catalog.xml"));

          // 接下來(lái),創(chuàng)建一個(gè) XMLStreamWriter
          XMLStreamWriter XMLStreamWriterr=outputFactory.createXMLStreamWriter(output);

          // 添加要在 XML 聲明中指定的編碼和版本(記住,指定的編碼并不是生成的 XML 文檔的編碼)。如果你需要指定 XML 文檔的編碼,該怎么辦呢?當(dāng)從一個(gè) XMLOutputFactory 對(duì)象創(chuàng)建一個(gè) XMLStreamWriter 對(duì)象時(shí),你會(huì)這樣做:

          XMLStreamWriter.writeStartDocument("UTF-8"
          "1.0");

          // 使用 writeComment() 方法以輸出一個(gè)注釋?zhuān)?/span>
          XMLStreamWriter.writeComment("A OReilly Journal Catalog");

          // 使用 writeProcessingInstruction() 方法以輸出一條處理指令:
          XMLStreamWriter.writeProcessingInstruction("catalog"
          "journal='OReilly'");

          // 使用 writeStartElement() 方法以輸出 'catalog' 元素的開(kāi)始(元素前綴和命名空間 URI 也可以在這個(gè)方法中指定的):
          XMLStreamWriter.writeStartElement("journal"
          "catalog" "http://OnJava.com/Journal");


          //
          使用 writeNamespace() 方法以添加 'journal' 命名空間聲明(命名空間前綴和命名空間 URI 也是在這個(gè)方法中指定的):
          XMLStreamWriter.writeNamespace("journal"
          "http://OnJava.com/Journal");

          ?

          // 再次使用 writeNamespace() 方法添加 xsi 命名空間:
          XMLStreamWriter.writeNamespace("xsi"
          "http://www.w3.org/2001/XMLSchema-instance");


          //
          使用 writeAttribute() 方法添加 xsi:namespaceSchemaLocation 屬性:
          XMLStreamWriter.writeAttribute("xsi:noNamespaceSchemaLocation"
          "file://c:/Schemas/catalog.xsd");


          //
          使用 writeAttribute() 方法添加 'publisher' 屬性:
          XMLStreamWriter.writeAttribute("publisher"
          "OReilly");

          ?

          // 輸出 'journal' 元素的開(kāi)始。當(dāng)增加一個(gè)新元素時(shí),前一個(gè)元素的 '>' 括號(hào)也被添加上:
          XMLStreamWriter.writeStartElement("journal"
          "journal" "http:
          //OnJava.com/Journal");


          //
          使用 writeAttribute() 方法以添加 'date' 'title' 屬性。然后,使用 writeElement() 方法以添加 'article' 'title' 元素。然后,使用 writeCharacters() 方法輸出 'title' 元素的文本:
          XMLStreamWriter.writeCharacters("Data Binding with XMLBeans");

          // 任何包含文本或子元素的元素都要有一個(gè)結(jié)束標(biāo)簽。使用 writeEndElement() 元素來(lái)添加 'title' 元素的結(jié)束標(biāo)簽:
          XMLStreamWriter.writeEndElement();

          // 添加 'author' 元素和 'journal' 元素的結(jié)束標(biāo)簽。在 writeEndElement() 方法中,不必要指定元素前綴和命名空間 URI 。以類(lèi)似方式添加另一個(gè) 'journal' 元素。然后,添加 'catalog' 元素的結(jié)束標(biāo)簽。最后,輸出緩沖的數(shù)據(jù):

          XMLStreamWriter.flush();

          // 最后一步,關(guān)閉 XMLStreamWriter
          XMLStreamWriter.close();

          ?

          // 這就是生成 catalog.xml 的過(guò)程。

          源碼中的列表 2 展示了完整的 Java 應(yīng)用程序 -XMLWriter.java 。這個(gè)應(yīng)用程序可以作為一個(gè)命令行應(yīng)用程序運(yùn)行或在一種例如 Eclipse 這樣的 IDE 中運(yùn)行。

          ?

          五、使用 XMLStreamReader 進(jìn)行分析

          ?

          ?

          通過(guò)使用 XMLStreamReader API 分析列表 1 中的文檔,我們來(lái)詳細(xì)分析一下其工作原理。 XMLStreamReader 使用一種光標(biāo)分析 XML 文檔。它的接口包含一個(gè) next() 方法 - 由它分析下一個(gè)分析事件。 getEventType() 方法返回事件類(lèi)型。后面的代碼片斷來(lái)自于 XMLParser.java 應(yīng)用程序,詳見(jiàn)列表 3

          在這個(gè) XMLParser.java 應(yīng)用程序中,首先,你要導(dǎo)入 StAX 類(lèi):

          import javax.xml.stream.*;
          import javax.xml.stream.events.*;
          import javax.xml.stream.XMLInputFactory;

          // 創(chuàng)建一個(gè) XMLInputFactory ,由此你會(huì)得到一個(gè) XMLStreamReader
          XMLInputFactory inputFactory=XMLInputFactory.newInstance();

          // 創(chuàng)建一個(gè) InputStream ,作為一個(gè)輸入流,它描述了將被分析的文件。另外,還要從前面創(chuàng)建的 XMLInputFactory 對(duì)象中創(chuàng)建一個(gè) XMLStreamReader
          InputStream input=new FileInputStream(new File("C:/STAX/catalog.xml"));

          XMLStreamReader xmlStreamReader =inputFactory.createXMLStreamReader(input);

          // 如果更多分析事件可用, hasNext() 方法返回 true 。然后,使用 next() 方法獲得下一個(gè)分析事件:
          int event=xmlStreamReader.next();

          比較于 SAX 分析, StAX 分析的優(yōu)點(diǎn)是,一個(gè)分析事件可以被跳過(guò) - 通過(guò)調(diào)用 next() 方法 ,詳見(jiàn)下面的代碼。例如,如果分析事件類(lèi)型為 ENTITY_DECLARATION ,那么開(kāi)發(fā)者可以決定是要從當(dāng)前事件中獲得事件信息,還是檢索下一個(gè)事件:

          If(event.getEventType()==XMLStreamConstants.ENTITY_DECLARATION){
          ?????????????? int event=xmlStreamReader.next();
          }

          通過(guò)不調(diào)用 next() 方法,分析也可以被推遲。 next() 方法返回 int ,它代表了一個(gè)分析事件 - 通過(guò)使用一個(gè) XMLStreamConstants 常量指定。

          XMLStreamReader
          所返回的不同的事件類(lèi)型列舉于表格1中。

          事件類(lèi)型

          描述

          START_DOCUMENT

          一個(gè)文檔的開(kāi)始

          START_ELEMENT

          一個(gè)元素的開(kāi)始

          ATTRIBUTE

          一個(gè)元素屬性

          NAMESPACE

          一個(gè)命名空間聲明

          CHARACTERS

          字符可以是文本,或是一個(gè)空格

          COMMENT

          一個(gè)注釋

          SPACE

          可忽略的空格

          PROCESSING_INSTRUCTION

          處理指令

          DTD

          一個(gè) DTD

          ENTITY_REFERENCE

          一個(gè)實(shí)體參考

          CDATA

          Cdata 節(jié)

          END_ELEMENT

          結(jié)束元素

          END_DOCUMENT

          結(jié)束文檔

          ENTITY_DECLARATION

          一個(gè)實(shí)體聲明

          NOTATION_DECLARATION

          一個(gè)標(biāo)志聲明

          ?

          表格 1.XMLStreamReader 事件

          ?

          ?

          這些不同的分析事件能夠使你獲得 XML 文檔中的數(shù)據(jù)和元數(shù)據(jù)。如果分析事件類(lèi)型是 START_DOCUMENT ,那么你將使用 getEncoding() 方法獲得 XML 文檔中的指定編碼,而你將使用 getVersion() 方法返回 XML 文檔的 XML 版本。


          同樣,如果你在使用一個(gè) START_ELEMENT 事件類(lèi)型工作,那么你將使用 getPrefix() 方法來(lái)返回元素前綴并且使用 getNamespaceURI 來(lái)返回元素前綴命名空間或默認(rèn)命名空間。為了獲得元素的本地命名,你將使用 getLocalName() 方法并且使用 getAttributesCount() 方法獲得屬性數(shù)目。你將使用 getAttributePrefix(i) 方法得到一個(gè)指定的屬性索引 i 的屬性前綴,而使用 getAttributeNamespace(i) 方法取得屬性命名空間。使用 getAttributeLocalName(i) 方法獲得屬性本地命名,使用 getAttributeValue(i) 方法獲得屬性值。如果事件類(lèi)型是 CHARACTERS COMMENT ,則使用 getText() 方法獲得相應(yīng)的文本。

          列表 4 顯示了示例 XML 文檔, catalog.xml 的分析輸出結(jié)果。

          列表 3 顯示了用于分析 XML 文檔的 Java 應(yīng)用程序。你可以從命令行上或在一種例如 Eclipse 這樣的 IDE 中來(lái)運(yùn)行該應(yīng)用程序。記住:如果你沒(méi)有首先運(yùn)行 XMLWriter.java 應(yīng)用程序而運(yùn)行 XMLParser.java( 見(jiàn)源碼中的列表 2) ,那么你需要把 catalog.xml( 見(jiàn)源碼中的列表 1) 復(fù)制到 C:/StAX 目錄下。

          ?

          ?

          六、使用 XMLEventReader 進(jìn)行分析

          ?

          本節(jié)將向你展示如何使用 XMLEventReader 來(lái)分析 catalog.xml XMLEventReader 接口使用一個(gè)事件對(duì)象迭代算子分析一個(gè) XML 文檔;通過(guò)這種方式,一個(gè) XML 事件生成一個(gè) XMLEvent 對(duì)象。 XMLEventReader 類(lèi)似于 XMLStreamReader- 分析事件是由 StAX 分析器生成的。然而, XMLEventReader XMLStreamReader 有一個(gè)優(yōu)點(diǎn):通過(guò)使用 XMLEventReader ,一個(gè)應(yīng)用程序可以使用 peek() 方法來(lái) " 偷看 " 下一個(gè)事件,而不必從流中讀取事件。這樣,一個(gè)應(yīng)用程序客戶(hù)端可以決定是否有必要分析下一個(gè)事件。 本節(jié)中的代碼片斷節(jié)選自 XMLEventParser.java 應(yīng)用程序,請(qǐng)參見(jiàn)列表 5

          首先,導(dǎo)入 StAX 類(lèi):

          import javax.xml.stream.*;
          import javax.xml.stream.events.*;
          import javax.xml.stream.XMLInputFactory;
            接下來(lái),創(chuàng)建一個(gè) XMLInputFactory ,由它獲得一個(gè) XMLEventReader 對(duì)象:

          XMLInputFactory inputFactory=XMLInputFactory.newInstance();
          InputStream input=new FileInputStream(new File("C:/STAX/catalog.xml"));
          XMLEventReader xmlEventReader =inputFactory.createXMLEventReader(input);
            在 StAX 中, XML 文檔事件是通過(guò) XMLEvent 對(duì)象描述的。使用 nextEvent() 方法來(lái)遍歷 XMLEventReader 對(duì)象以獲得下一個(gè)事件:

          XMLEvent event=xmlEventReader.nextEvent();
            使用 getEventType() 方法來(lái)獲得事件類(lèi)型 ( 請(qǐng)參考表格1 ) XMLEvent 接口還提供布爾方法來(lái)獲得事件類(lèi)型。例如, isStartDocum ent() 返回 true ,如果事件是開(kāi)始文檔類(lèi)型。在下列代碼中,事件是開(kāi)始元素類(lèi)型,因此一個(gè) StartElement 對(duì)象可以從這個(gè) XMLEvent 接口獲得:

          if(event.isStartElement()){
            StartElement startElement=event.asStartElement();
          }
            使用 getAttributes() 方法獲得元素屬性:

          Iterator attributes=startElement.getAttributes();
            這個(gè) Iterator 描述了一個(gè) javax.xml.stream.events.Attribute 對(duì)象。使用 next() 方法遍歷該 Iterator

          Attribute attribute=(javax.xml.stream.events.Attribute)(attributes.next());
            最后,使用 getName() 方法獲得屬性命名,使用 getValue() 方法獲得屬性值。


          列表 5 顯示出分析該 XML 文檔的 Java 應(yīng)用程序。應(yīng)用程序 XMLEventReader 可以作為一個(gè)命令行應(yīng)用程序運(yùn)行,或在一種例如 Eclipse 這樣的 IDE 中運(yùn)行。記住:如果你運(yùn)行 XMLWriter.java XMLParser.java 應(yīng)用程序而不首先運(yùn)行 XMLEventParser.java 應(yīng)用程序,那么你將需要把 catalog.xml 復(fù)制到 C:/StAX 目錄下。

          最終,基于拉的事件生成把事件規(guī)則提供到分析器應(yīng)用程序而不是提供到分析器。

          ?

          posted on 2006-09-17 22:24 Lizzie 閱讀(439) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): 專(zhuān)業(yè)積木

          FeedBack:
          # re: 在JDK 6.0中基于StAX分析XML數(shù)據(jù)
          2009-11-23 07:05 | anon
          you might also want to look at vtd-xml, the latest and most advanced XML processing API available today

          http://vtd-xml.sf.net  回復(fù)  更多評(píng)論
            

          <2006年9月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          常用鏈接

          留言簿(1)

          隨筆分類(lèi)

          隨筆檔案

          文章分類(lèi)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 巨鹿县| 扎鲁特旗| 泰宁县| 双鸭山市| 白沙| 泽州县| 德保县| 秀山| 浙江省| 邵阳县| 格尔木市| 高淳县| 资溪县| 清原| 绥中县| 武汉市| 武胜县| 平顶山市| 临泉县| 章丘市| 佳木斯市| 浦江县| 绥化市| 黔西| 丰台区| 江达县| 桃园县| 浪卡子县| 麻江县| 武城县| 宜丰县| 斗六市| 樟树市| 会泽县| 应城市| 娱乐| 辽阳市| 泰安市| 湖南省| 鹤庆县| 西宁市|