在JDK 6.0中基於StAX分析XML數據

 
J2EE/XML開發者通常都是使用文檔對象模型(DOM)API或簡單的API for XML(SAX) API來分析XML文檔。然而,這些API都有其缺點。其中,DOM API的缺點之一是消耗大量的內存,因爲在該XML文檔可以被導航之前,必須創建一個完整的XML文檔的內存結構。而SAX API的缺點在於,它實例了一種推分析模型API,其中分析事件是由分析器生成的。比較之下,StAX則是基於一種拉分析模型。在本文中,你將首先創建你自己的XML文檔,然後學習使用各種不同方法來對之進行分析;最後,我們使用事件生成的StAX拉方法。

  一、推分析之於拉分析

  比較於推分析,拉分析具有如下一些優點:

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

  2. 拉分析的代碼更簡單並且它比推分析有更少的庫。

  3. 拉分析客戶端能同時讀多個XML文檔。

  4. 拉分析允許你過濾XML文檔並且跳過分析事件。

  二、瞭解StAX

  針對於XML的流式API(StAX),是在2004年3月的JSR 173規範中引入,這是一種針對XML的流式拉分析API。StAX是JDK 6.0提供的一種新特徵,你可以從此處下載它的測試版本試用。

  一個推模型分析器不斷地生成事件,直到XML文檔被完全分析結束。但是,拉分析由應用程序進行調整;因此,分析事件是由應用程序生成的。這意味着,使用StaX,你可以推遲分析-在分析時跳過元素並且分析多個文檔。在使用DOM API的時候,你必須把整個的XML文檔分析成一棵DOM結構,這樣也就降低了分析效率。而藉助於StAX,在分析XML文檔時生成分析事件。有關於StAX分析器與其它分析器的比較在此不多介紹。

  StAX API的實現是使用了Java Web服務開發(JWSDP)1.6,並結合了Sun Java流式XML分析器(SJSXP)-它位於javax.xml.stream包中。XMLStreamReader接口用於分析一個XML文檔,而XMLStreamWriter接口用於生成一個XML文檔。XMLEventReader負責使用一個對象事件迭代子分析XML事件-這與XMLStreamReader所使用的光標機制形成對照。本教程將基於JDK 6.0中的StAX實現來完成對一個XML文檔的分析。

  其實,StaX僅僅是JDK 6.0所提供的XML新特徵之一。新的JDK 6.0還提供了對針對於XML-Web服務的Java架構(JAX-WS)2.0,針對於XML綁定的Java API(JAXB) 2.0,XML數字簽名API的支持,甚至還支持SQL:2003 'XML'數據類型。

  三、初步安裝

  如果你正在使用JDK 6.0,那麼默認情況下,StAX API位於Classpath中。如果你在使用JWSDP 1.6,請把JWSDP 1.6 StAX API添加到classpath中。這需要把<jwsdp-1.6>/sjsxp/lib/ jsr173_api.jar和<jwsdp-1.6>/sjsxp/lib/sjsxp.jar添加到CLASSPATH變量中。在<jwsdp-1.6>目錄下安裝JWSDP 1.6。Jsr173_api.jar相應於JSR-173 API JAR,Sjsxp.jar相應於SJXSP實現JAR。
四、使用XMLStreamWriter進行寫操作

  首先,你要創建將待分析的XML文檔。由StAX的XMLStreamWriter生成XML。然而,XMLStreamWriter的一個限制是,它不一定會生成良構的文檔-而且生成的文檔也不一定是有效的。你需要確保生成的XML文檔是良構的。列表1是一個由XMLStreamWriter生成的原始XML文檔的示例。

  在此,你試圖使用XMLStreamWriter API生成列表1中的catalog.xml。在本節中的代碼片斷節選自XMLWriter.java應用程序,顯示於列表2中。首先,你將導入StAX包類,請參考下列編碼:
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import javax.xml.stream.XMLOutputFactory;

  你要從一個XMLOutputFactory中得到你的XMLStreamWriter。因此,首先你必須創建一個新的XMLOutputFactory:
XMLOutputFactory outputFactory=XMLOutputFactory.newInstance();

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

  接下來,創建一個XMLStreamWriter:
XMLStreamWriter XMLStreamWriterr=outputFactory.createXMLStreamWriter(output);

  現在,使用writeStartDocument()方法創建一個文檔開頭。添加要在XML聲明中指定的編碼和版本(記住,指定的編碼並不是生成的XML文檔的編碼)。如果你需要指定XML文檔的編碼,該怎麼辦呢?當從一個XMLOutputFactory對象創建一個XMLStreamWriter對象時,你會這樣做:
XMLStreamWriter.writeStartDocument("UTF-8""1.0");

  使用writeComment()方法以輸出一個註釋:
XMLStreamWriter.writeComment("A OReilly Journal Catalog");

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

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

  使用writeNamespace()方法以添加'journal'命名空間聲明(命名空間前綴和命名空間URI也是在這個方法中指定的):
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'元素的開始。當增加一個新元素時,前一個元素的'>'括號也被添加上:
XMLStreamWriter.writeStartElement("journal""journal""http:
//OnJava.com/Journal");

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

  任何包含文本或子元素的元素都要有一個結束標籤。使用writeEndElement()元素來添加'title'元素的結束標籤:
XMLStreamWriter.writeEndElement();

  添加'author'元素和'journal'元素的結束標籤。在writeEndElement()方法中,不必要指定元素前綴和命名空間URI。以類似方式添加另一個'journal'元素。然後,添加'catalog'元素的結束標籤。最後,輸出緩衝的數據:
XMLStreamWriter.flush();

  最後一步,關閉XMLStreamWriter。
XMLStreamWriter.close();

  這就是生成catalog.xml的過程。

  源碼中的列表2展示了完整的Java應用程序-XMLWriter.java。這個應用程序可以作爲一個命令行應用程序運行或在一種例如Eclipse這樣的IDE中運行。
五、使用XMLStreamReader進行分析

  通過使用XMLStreamReader API分析列表1中的文檔,我們來詳細分析一下其工作原理。XMLStreamReader使用一種光標分析XML文檔。它的接口包含一個next()方法-由它分析下一個分析事件。getEventType()方法返回事件類型。後面的代碼片斷來自於XMLParser.java應用程序,詳見列表3。

  在這個XMLParser.java應用程序中,首先,你要導入StAX類:
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import javax.xml.stream.XMLInputFactory;

  然後,創建一個XMLInputFactory,由此你會得到一個XMLStreamReader:
XMLInputFactory inputFactory=XMLInputFactory.newInstance();

  現在,你需要創建一個InputStream,作爲一個輸入流,它描述了將被分析的文件。另外,還要從前面創建的XMLInputFactory對象中創建一個XMLStreamReader。
InputStream input=new FileInputStream(new File("C:/STAX/catalog.xml"));
XMLStreamReader xmlStreamReader =inputFactory.createXMLStreamReader(input);

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

  比較於SAX分析,StAX分析的優點是,一個分析事件可以被跳過-通過調用next()方法,詳見下面的代碼。例如,如果分析事件類型爲ENTITY_DECLARATION,那麼開發者可以決定是要從當前事件中獲得事件信息,還是檢索下一個事件:
If(event.getEventType()==XMLStreamConstants.ENTITY_DECLARATION){
int event=xmlStreamReader.next();
}

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

  XMLStreamReader所返回的不同的事件類型列舉於表格1中。
事件類型
描述
START_DOCUMENT
一個文檔的開始
START_ELEMENT
一個元素的開始
ATTRIBUTE
一個元素屬性
NAMESPACE
一個命名空間聲明
CHARACTERS
字符可以是文本,或是一個空格
COMMENT
一個註釋
SPACE
可忽略的空格
PROCESSING_INSTRUCTION
處理指令
DTD
一個DTD
ENTITY_REFERENCE
一個實體參考
CDATA
Cdata
END_ELEMENT
結束元素
END_DOCUMENT
結束文檔
ENTITY_DECLARATION
一個實體聲明
NOTATION_DECLARATION
一個標誌聲明
        表格1.XMLStreamReader事件

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

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

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

  列表3顯示了用於分析XML文檔的Java應用程序。你可以從命令行上或在一種例如Eclipse這樣的IDE中來運行該應用程序。記住:如果你沒有首先運行XMLWriter.java應用程序而運行XMLParser.java(見源碼中的列表2),那麼你需要把catalog.xml(見源碼中的列表1)複製到C:/StAX目錄下。
六、使用XMLEventReader進行分析

  本節將向你展示如何使用XMLEventReader來分析catalog.xml。XMLEventReader接口使用一個事件對象迭代算子分析一個XML文檔;通過這種方式,一個XML事件生成一個XMLEvent對象。XMLEventReader類似於XMLStreamReader-分析事件是由StAX分析器生成的。然而,XMLEventReader比XMLStreamReader有一個優點:通過使用XMLEventReader,一個應用程序可以使用peek()方法來"偷看"下一個事件,而不必從流中讀取事件。這樣,一個應用程序客戶端可以決定是否有必要分析下一個事件。本節中的代碼片斷節選自XMLEventParser.java應用程序,請參見列表5。

  首先,導入StAX類:
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import javax.xml.stream.XMLInputFactory;

  接下來,創建一個XMLInputFactory,由它獲得一個XMLEventReader對象:
XMLInputFactory inputFactory=XMLInputFactory.newInstance();
InputStream input=new FileInputStream(new File("C:/STAX/catalog.xml"));
XMLEventReader xmlEventReader =inputFactory.createXMLEventReader(input);

  在StAX中,XML文檔事件是通過XMLEvent對象描述的。使用nextEvent()方法來遍歷XMLEventReader對象以獲得下一個事件:
XMLEvent event=xmlEventReader.nextEvent();

  使用getEventType()方法來獲得事件類型(請參考表格1)。XMLEvent接口還提供布爾方法來獲得事件類型。例如,isStartDocument()返回true,如果事件是開始文檔類型。在下列代碼中,事件是開始元素類型,因此一個StartElement對象可以從這個XMLEvent接口獲得:
if(event.isStartElement()){
 StartElement startElement=event.asStartElement();
}

  使用getAttributes()方法獲得元素屬性:
Iterator attributes=startElement.getAttributes();

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

  最後,使用getName()方法獲得屬性命名,使用getValue()方法獲得屬性值。

  列表5顯示出分析該XML文檔的Java應用程序。應用程序XMLEventReader可以作爲一個命令行應用程序運行,或在一種例如Eclipse這樣的IDE中運行。記住:如果你運行XMLWriter.java或XMLParser.java應用程序而不首先運行XMLEventParser.java應用程序,那麼你將需要把catalog.xml複製到C:/StAX目錄下。

  最終,基於拉的事件生成把事件規則提供到分析器應用程序而不是提供到分析器。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章