?
J2EE/XML
開發(fā)者通常都是使用文檔對象模型
(DOM)API
或簡單的
API for XML(SAX) API
來分析
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í)使用各種不同方法來對之進(jìn)行分析;最后,我們使用事件生成的
StAX
拉方法。
一、
Push
推分析之于拉分析
Pull
?
比較于推分析,拉分析具有如下一些優(yōu)點(diǎn):
1.
在拉分析中,事件是由分析應(yīng)用程序生成的,因此把分析規(guī)則提供到客戶端而不是分析器。
2.
拉分析的代碼更簡單并且它比推分析有更少的庫。
3.
拉分析客戶端能同時(shí)讀多個(gè)
XML
文檔。
4.
拉分析允許你過濾
XML
文檔并且跳過分析事件。
二、了解
StAX
針對于
XML
的流式
API(StAX)
,是在
2004
年
3
月的
JSR 173
規(guī)范中引入,這是一種針對
XML
的流式拉分析
API
。
StAX
是
JDK 6.0
提供的一種新特征,你可以從此處下載它的測試版本試用。
一個(gè)推模型分析器不斷地生成事件,直到
XML
文檔被完全分析結(jié)束。但是,拉分析由應(yīng)用程序進(jìn)行調(diào)整;因此,分析事件是由應(yīng)用程序生成的。這意味著,使用
StaX
,你可以推遲分析
-
在分析時(shí)跳過元素并且分析多個(gè)文檔。在使用
DOM API
的時(shí)候,你必須把整個(gè)的
XML
文檔分析成一棵
DOM
結(jié)構(gòu),這樣也就降低了分析效率。而借助于
StAX
,在分析
XML
文檔時(shí)生成分析事件。有關(guān)于
StAX
分析器與其它分析器的比較在此不多介紹。
StAX API
的實(shí)現(xiàn)是使用了
Java Web
服務(wù)開發(fā)(
JWSDP
)
1.6
,并結(jié)合了
Sun Java
流式
XML
分析器
(SJSXP)-
它位于
javax.xml.stream
包中。
XMLStreamReader
接口用于分析一個(gè)
XML
文檔,而
XMLStreamWriter
接口用于生成一個(gè)
XML
文檔。
XMLEventReader
負(fù)責(zé)使用一個(gè)對象事件迭代子分析
XML
事件
-
這與
XMLStreamReader
所使用的光標(biāo)機(jī)制形成對照。本教程將基于
JDK 6.0
中的
StAX
實(shí)現(xiàn)來完成對一個(gè)
XML
文檔的分析。
其實(shí),
StaX
僅僅是
JDK 6.0
所提供的
XML
新特征之一。新的
JDK 6.0
還提供了對針對于
XML-Web
服務(wù)的
Java
架構(gòu)(
JAX-WS
)
2.0
,針對于
XML
綁定的
Java API(JAXB) 2.0
,
XML
數(shù)字簽名
API
的支持,甚至還支持
SQL:2003 'XML'
數(shù)據(jù)類型。
三、安裝
?
如果你正在使用
JDK 6.0
,那么默認(rèn)情況下,
StAX API
位于
Classpath
中。如果你在使用
JWSDP 1.6
,請把
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)行寫操作
?
?
首先,你要?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
包類,請參考下列編碼:
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import javax.xml.stream.XMLOutputFactory;
//
首先你必須創(chuàng)建一個(gè)新的
XMLOutputFactory
XMLOutputFactory outputFactory=XMLOutputFactory.newInstance();
//
接下來,創(chuàng)建一個(gè)
FileWriter
以輸出
XML
文檔
-
它將被生成到一個(gè)
XML
文件中:
FileWriter output=new FileWriter(new File("C:/STAX/catalog.xml"));
//
接下來,創(chuàng)建一個(gè)
XMLStreamWriter
:
XMLStreamWriter XMLStreamWriterr=outputFactory.createXMLStreamWriter(output);
//
添加要在
XML
聲明中指定的編碼和版本(記住,指定的編碼并不是生成的
XML
文檔的編碼)。如果你需要指定
XML
文檔的編碼,該怎么辦呢?當(dāng)從一個(gè)
XMLOutputFactory
對象創(chuàng)建一個(gè)
XMLStreamWriter
對象時(shí),你會(huì)這樣做:
XMLStreamWriter.writeStartDocument("UTF-8"
,
"1.0");
//
使用
writeComment()
方法以輸出一個(gè)注釋:
XMLStreamWriter.writeComment("A OReilly Journal Catalog");
//
使用
writeProcessingInstruction()
方法以輸出一條處理指令:
XMLStreamWriter.writeProcessingInstruction("catalog"
,
"journal='OReilly'");
//
使用
writeStartElement()
方法以輸出
'catalog'
元素的開始(元素前綴和命名空間
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'
元素的開始。當(dāng)增加一個(gè)新元素時(shí),前一個(gè)元素的
'>'
括號也被添加上:
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()
元素來添加
'title'
元素的結(jié)束標(biāo)簽:
XMLStreamWriter.writeEndElement();
//
添加
'author'
元素和
'journal'
元素的結(jié)束標(biāo)簽。在
writeEndElement()
方法中,不必要指定元素前綴和命名空間
URI
。以類似方式添加另一個(gè)
'journal'
元素。然后,添加
'catalog'
元素的結(jié)束標(biāo)簽。最后,輸出緩沖的數(shù)據(jù):
XMLStreamWriter.flush();
//
最后一步,關(guān)閉
XMLStreamWriter
。
XMLStreamWriter.close();
?
//
這就是生成
catalog.xml
的過程。
源碼中的列表
2
展示了完整的
Java
應(yīng)用程序
-XMLWriter.java
。這個(gè)應(yīng)用程序可以作為一個(gè)命令行應(yīng)用程序運(yùn)行或在一種例如
Eclipse
這樣的
IDE
中運(yùn)行。
?
五、使用
XMLStreamReader
進(jìn)行分析
?
?
通過使用
XMLStreamReader API
分析列表
1
中的文檔,我們來詳細(xì)分析一下其工作原理。
XMLStreamReader
使用一種光標(biāo)分析
XML
文檔。它的接口包含一個(gè)
next()
方法
-
由它分析下一個(gè)分析事件。
getEventType()
方法返回事件類型。后面的代碼片斷來自于
XMLParser.java
應(yīng)用程序,詳見列表
3
。
在這個(gè)
XMLParser.java
應(yīng)用程序中,首先,你要導(dǎo)入
StAX
類:
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
對象中創(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è)分析事件可以被跳過
-
通過調(diào)用
next()
方法
,詳見下面的代碼。例如,如果分析事件類型為
ENTITY_DECLARATION
,那么開發(fā)者可以決定是要從當(dāng)前事件中獲得事件信息,還是檢索下一個(gè)事件:
If(event.getEventType()==XMLStreamConstants.ENTITY_DECLARATION){
?????????????? int event=xmlStreamReader.next();
}
通過不調(diào)用
next()
方法,分析也可以被推遲。
next()
方法返回
int
,它代表了一個(gè)分析事件
-
通過使用一個(gè)
XMLStreamConstants
常量指定。
XMLStreamReader
所返回的不同的事件類型列舉于表格1中。
事件類型
|
描述
|
START_DOCUMENT
|
一個(gè)文檔的開始
|
START_ELEMENT
|
一個(gè)元素的開始
|
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ù)。如果分析事件類型是
START_DOCUMENT
,那么你將使用
getEncoding()
方法獲得
XML
文檔中的指定編碼,而你將使用
getVersion()
方法返回
XML
文檔的
XML
版本。
同樣,如果你在使用一個(gè)
START_ELEMENT
事件類型工作,那么你將使用
getPrefix()
方法來返回元素前綴并且使用
getNamespaceURI
來返回元素前綴命名空間或默認(rèn)命名空間。為了獲得元素的本地命名,你將使用
getLocalName()
方法并且使用
getAttributesCount()
方法獲得屬性數(shù)目。你將使用
getAttributePrefix(i)
方法得到一個(gè)指定的屬性索引
i
的屬性前綴,而使用
getAttributeNamespace(i)
方法取得屬性命名空間。使用
getAttributeLocalName(i)
方法獲得屬性本地命名,使用
getAttributeValue(i)
方法獲得屬性值。如果事件類型是
CHARACTERS
或
COMMENT
,則使用
getText()
方法獲得相應(yīng)的文本。
列表
4
顯示了示例
XML
文檔,
catalog.xml
的分析輸出結(jié)果。
列表
3
顯示了用于分析
XML
文檔的
Java
應(yīng)用程序。你可以從命令行上或在一種例如
Eclipse
這樣的
IDE
中來運(yùn)行該應(yīng)用程序。記住:如果你沒有首先運(yùn)行
XMLWriter.java
應(yīng)用程序而運(yùn)行
XMLParser.java(
見源碼中的列表
2)
,那么你需要把
catalog.xml(
見源碼中的列表
1)
復(fù)制到
C:/StAX
目錄下。
?
?
六、使用
XMLEventReader
進(jìn)行分析
?
本節(jié)將向你展示如何使用
XMLEventReader
來分析
catalog.xml
。
XMLEventReader
接口使用一個(gè)事件對象迭代算子分析一個(gè)
XML
文檔;通過這種方式,一個(gè)
XML
事件生成一個(gè)
XMLEvent
對象。
XMLEventReader
類似于
XMLStreamReader-
分析事件是由
StAX
分析器生成的。然而,
XMLEventReader
比
XMLStreamReader
有一個(gè)優(yōu)點(diǎn):通過使用
XMLEventReader
,一個(gè)應(yīng)用程序可以使用
peek()
方法來
"
偷看
"
下一個(gè)事件,而不必從流中讀取事件。這樣,一個(gè)應(yīng)用程序客戶端可以決定是否有必要分析下一個(gè)事件。
本節(jié)中的代碼片斷節(jié)選自
XMLEventParser.java
應(yīng)用程序,請參見列表
5
。
首先,導(dǎo)入
StAX
類:
import javax.xml.stream.*;
import javax.xml.stream.events.*;
import javax.xml.stream.XMLInputFactory;
接下來,創(chuàng)建一個(gè)
XMLInputFactory
,由它獲得一個(gè)
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
對象以獲得下一個(gè)事件:
XMLEvent event=xmlEventReader.nextEvent();
使用
getEventType()
方法來獲得事件類型
(
請參考表格1
)
。
XMLEvent
接口還提供布爾方法來獲得事件類型。例如,
isStartDocum ent()
返回
true
,如果事件是開始文檔類型。在下列代碼中,事件是開始元素類型,因此一個(gè)
StartElement
對象可以從這個(gè)
XMLEvent
接口獲得:
if(event.isStartElement()){
StartElement startElement=event.asStartElement();
}
使用
getAttributes()
方法獲得元素屬性:
Iterator attributes=startElement.getAttributes();
這個(gè)
Iterator
描述了一個(gè)
javax.xml.stream.events.Attribute
對象。使用
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 閱讀(434)
評論(1) 編輯 收藏 所屬分類:
專業(yè)積木