示例代码
原文: https://docs.oracle.com/javase/tutorial/jaxp/stax/example.html
本节逐步介绍 JAXP 参考实现包中包含的示例 StAX 代码。本节中使用的所有示例目录都位于 INSTALL_DIR / jaxp-
版本 / samples / stax
目录中。
本节涉及的主题如下:
示例代码组织
INSTALL_DIR / jaxp-
版本 / samples / stax
目录包含六个 StAX 示例目录:
光标示例:
光标
目录包含CursorParse.java
,它说明了如何使用XMLStreamReader
(光标)API 读取 XML 文件。Cursor-to-Event 示例:
cursor2event
目录包含CursorApproachEventObject.java
,它说明了应用程序如何以XMLEvent
获取信息使用游标 API 时的对象。事件示例:
事件
目录包含EventParse.java
,它说明了如何使用XMLEventReader
(事件迭代器)API 来读取 XML 文件。过滤器示例:
过滤器
目录包含MyStreamFilter.java
,它说明了如何使用 StAX 流过滤器 API。在此示例中,过滤器仅接受StartElement
和EndElement
事件,并过滤掉剩余的事件。读写示例:
readnwrite
目录包含EventProducerConsumer.java
,它说明了如何使用 StAX 生成器/消费者机制同时读写 XML 流。编写器示例:
编写器
目录包含CursorWriter.java
,它说明了如何使用XMLStreamWriter
以编程方式编写 XML 文件。
除 Writer 示例之外的所有 StAX 示例都使用示例 XML 文档BookCatalog.xml
。
示例 XML 文档
大多数 StAX 示例类使用的示例 XML 文档BookCatalog.xml
是一个基于常见BookCatalogue
命名空间的简单书籍目录。 BookCatalog.xml
的内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<BookCatalogue xmlns="http://www.publishing.org">
<Book>
<Title>Yogasana Vijnana: the Science of Yoga</Title>
<author>Dhirendra Brahmachari</Author>
<Date>1966</Date>
<ISBN>81-40-34319-4</ISBN>
<Publisher>Dhirendra Yoga Publications</Publisher>
<Cost currency="INR">11.50</Cost>
</Book>
<Book>
<Title>The First and Last Freedom</Title>
<Author>J. Krishnamurti</Author>
<Date>1954</Date>
<ISBN>0-06-064831-7</ISBN>
<Publisher>Harper & Row</Publisher>
<Cost currency="USD">2.95</Cost>
</Book>
</BookCatalogue>
游标示例
位于 INSTALL_DIR / jaxp-
版本 / samples / stax / cursor /
目录, CursorParse.java
演示使用 StAX 游标 API 读取 XML 文档。在 Cursor 示例中,应用程序通过调用next()
指示解析器读取 XML 输入流中的下一个事件。
请注意, next()
只返回一个整数常量,该常量对应于解析器所在的基础事件。应用程序需要调用相关函数以获取与基础事件相关的更多信息。
您可以将此方法想象为在 XML 输入流中移动的虚拟光标。当虚拟光标处于特定事件时,可以调用各种存取方法。
逐步完成活动
在此示例中,客户端应用程序通过在解析器上调用next
方法来提取 XML 流中的下一个事件;例如:
try {
for (int i = 0 ; i < count ; i++) {
// pass the file name.. all relative entity
// references will be resolved against this
// as base URI.
XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
new FileInputStream(filename));
// when XMLStreamReader is created,
// it is positioned at START_DOCUMENT event.
int eventType = xmlr.getEventType();
printEventType(eventType);
printStartDocument(xmlr);
// check if there are more events
// in the input stream
while(xmlr.hasNext()) {
eventType = xmlr.next();
printEventType(eventType);
// these functions print the information
// about the particular event by calling
// the relevant function
printStartElement(xmlr);
printEndElement(xmlr);
printText(xmlr);
printPIData(xmlr);
printComment(xmlr);
}
}
}
注意, next
只返回一个对应于当前光标位置的事件的整数常量。应用程序调用相关函数以获取与基础事件相关的更多信息。当光标处于特定事件时,可以调用各种存取方法。
返回字符串表示
因为next
方法只返回与底层事件类型对应的整数,所以通常需要将这些整数映射到事件的字符串表示;例如:
public final static String getEventTypeString(int eventType) {
switch (eventType) {
case XMLEvent.START_ELEMENT:
return "START_ELEMENT";
case XMLEvent.END_ELEMENT:
return "END_ELEMENT";
case XMLEvent.PROCESSING_INSTRUCTION:
return "PROCESSING_INSTRUCTION";
case XMLEvent.CHARACTERS:
return "CHARACTERS";
case XMLEvent.COMMENT:
return "COMMENT";
case XMLEvent.START_DOCUMENT:
return "START_DOCUMENT";
case XMLEvent.END_DOCUMENT:
return "END_DOCUMENT";
case XMLEvent.ENTITY_REFERENCE:
return "ENTITY_REFERENCE";
case XMLEvent.ATTRIBUTE:
return "ATTRIBUTE";
case XMLEvent.DTD:
return "DTD";
case XMLEvent.CDATA:
return "CDATA";
case XMLEvent.SPACE:
return "SPACE";
}
return "UNKNOWN_EVENT_TYPE , " + eventType;
}
运行游标示例
编译并运行光标示例,在终端窗口中,转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac stax/cursor/*.java
Run the
CursorParse
sample on theBookCatalogue.xml
file, with the following command.CursorParse
将打印出BookCatalogue.xml
文件的每个元素。
java stax/event/CursorParse stax/data/BookCatalogue.xml
游标到事件示例
位于 tut-install / javaeetutorial5 / examples / stax / cursor2event /
目录中, CursorApproachEventObject.java
演示如何获取XMLEvent 返回的信息
对象即使使用游标 API 也是如此。
这里的想法是游标 API 的XMLStreamReader
返回对应于特定事件的整数常量,而事件迭代器 API 的XMLEventReader
返回不可变和持久的事件对象。 XMLStreamReader
效率更高,但XMLEventReader
更易于使用,因为与特定事件相关的所有信息都封装在返回的XMLEvent
对象中。但是,事件方法的缺点是为每个事件创建对象的额外开销,这会消耗时间和内存。
有了这个想法,即使使用游标 API, XMLEventAllocator
也可以用作XMLEvent
对象获取事件信息。
实例化 XMLEventAllocator
第一步是创建一个新的XMLInputFactory
并实例化一个XMLEventAllocator
:
XMLInputFactory xmlif = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + xmlif);
xmlif.setEventAllocator(new XMLEventAllocatorImpl());
allocator = xmlif.getEventAllocator();
XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
new FileInputStream(filename));
创建事件迭代器
下一步是创建一个事件迭代器:
int eventType = xmlr.getEventType();
while (xmlr.hasNext()) {
eventType = xmlr.next();
// Get all "Book" elements as XMLEvent object
if (eventType == XMLStreamConstants.START_ELEMENT
&& xmlr.getLocalName().equals("Book")) {
// get immutable XMLEvent
StartElement event = getXMLEvent(xmlr).asStartElement();
System.out.println ("EVENT: " + event.toString());
}
}
创建分配器方法
最后一步是创建XMLEventAllocator
方法:
private static XMLEvent getXMLEvent(XMLStreamReader reader)
throws XMLStreamException {
return allocator.allocate(reader);
}
运行光标到事件示例
要编译并运行游标到事件示例,在终端窗口中,转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac -classpath ../lib/jaxp-ri.jar stax/cursor2event/*.java
Run the
CursorApproachEventObject
sample on theBookCatalogue.xml
file, with the following command.java stax/cursor2event/CursorApproachEventObject stax/data/BookCatalogue.xml
CursorApproachEventObject
将打印出BookCatalogue.xml
文件定义的事件列表。
事件示例
位于 INSTALL_DIR / jaxp-
版本 / samples / stax / event /
目录, EventParse.java
演示了如何使用 StAX 事件 API 来读取 XML 文档。
创建输入工厂
第一步是创建XMLInputFactory
的新实例:
XMLInputFactory factory = XMLInputFactory.newInstance();
System.out.println("FACTORY: " + factory);
创建事件阅读器
下一步是创建XMLEventReader
的实例:
XMLEventReader r = factory.createXMLEventReader
(filename, new FileInputStream(filename));
创建事件迭代器
第三步是创建一个事件迭代器:
XMLEventReader r = factory.createXMLEventReader
(filename, new FileInputStream(filename));
while (r.hasNext()) {
XMLEvent e = r.nextEvent();
System.out.println(e.toString());
}
获得活动流
最后一步是获取基础事件流:
public final static String getEventTypeString(int eventType) {
switch (eventType) {
case XMLEvent.START_ELEMENT:
return "START_ELEMENT";
case XMLEvent.END_ELEMENT:
return "END_ELEMENT";
case XMLEvent.PROCESSING_INSTRUCTION:
return "PROCESSING_INSTRUCTION";
case XMLEvent.CHARACTERS:
return "CHARACTERS";
case XMLEvent.COMMENT:
return "COMMENT";
case XMLEvent.START_DOCUMENT:
return "START_DOCUMENT";
case XMLEvent.END_DOCUMENT:
return "END_DOCUMENT";
case XMLEvent.ENTITY_REFERENCE:
return "ENTITY_REFERENCE";
case XMLEvent.ATTRIBUTE:
return "ATTRIBUTE";
case XMLEvent.DTD:
return "DTD";
case XMLEvent.CDATA:
return "CDATA";
case XMLEvent.SPACE:
return "SPACE";
}
return "UNKNOWN_EVENT_TYPE," + eventType;
}
返回输出
当您运行 Event 示例时,将编译EventParse
类,并将 XML 流解析为事件并返回到STDOUT
。例如,作者
元素的实例返回为:
<[’http://www.publishing.org’]::Author>
Dhirendra Brahmachari
</[’http://www.publishing.org’]::Author>
请注意,在此示例中,事件包含一个开始和结束标记,两者都包含命名空间。元素的内容在标记内作为字符串返回。
类似地, Cost
元素的实例返回为:
<[’http://www.publishing.org’]::Cost currency=’INR’>
11.50
</[’http://www.publishing.org’]::Cost
在这种情况下,货币
属性和值将在事件的开始标记中返回。
运行事件示例
要编译并运行事件示例,在终端窗口中,转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
Run the
EventParse
sample on theBookCatalogue.xml
file, with the following command.java stax/event/EventParse stax/data/BookCatalogue.xml
EventParse
将打印出来自BookCatalogue.xml
文件定义的所有元素的数据。
过滤器示例
位于 INSTALL_DIR / jaxp-
版本 / samples / stax / filter /
目录, MyStreamFilter.java
演示了如何使用 StAX 流过滤器 API 过滤掉应用程序不需要的事件。在此示例中,解析器筛选出除StartElement
和EndElement
之外的所有事件。
实现 StreamFilter 类
MyStreamFilter
类实现javax.xml.stream.StreamFilter
:
public class MyStreamFilter implements javax.xml.stream.StreamFilter {
// ...
}
创建输入工厂
下一步是创建XMLInputFactory
的实例。在这种情况下,工厂也会设置各种属性:
XMLInputFactory xmlif = null ;
try {
xmlif = XMLInputFactory.newInstance();
xmlif.setProperty(
XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
Boolean.TRUE);
xmlif.setProperty(
XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
Boolean.FALSE);
xmlif.setProperty(
XMLInputFactory.IS_NAMESPACE_AWARE,
Boolean.TRUE);
xmlif.setProperty(
XMLInputFactory.IS_COALESCING,
Boolean.TRUE);
}
catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("FACTORY: " + xmlif);
System.out.println("filename = "+ filename);
创建过滤器
下一步是实例化文件输入流并创建流过滤器:
FileInputStream fis = new FileInputStream(filename);
XMLStreamReader xmlr = xmlif.createFilteredReader(
xmlif.createXMLStreamReader(fis),
new MyStreamFilter());
int eventType = xmlr.getEventType();
printEventType(eventType);
while (xmlr.hasNext()) {
eventType = xmlr.next();
printEventType(eventType);
printName(xmlr,eventType);
printText(xmlr);
if (xmlr.isStartElement()) {
printAttributes(xmlr);
}
printPIData(xmlr);
System.out.println("-----------------------");
}
捕获事件流
下一步是捕获事件流。这基本上与事件示例中的方式相同。
过滤流
最后一步是过滤流:
public boolean accept(XMLStreamReader reader) {
if (!reader.isStartElement() && !reader.isEndElement())
return false;
else
return true;
}
返回输出
当您运行 Filter 示例时,将编译MyStreamFilter
类,并将 XML 流解析为事件并返回到STDOUT
。例如,作者
事件返回如下:
EVENT TYPE(1):START_ELEMENT
HAS NAME: Author
HAS NO TEXT
HAS NO ATTRIBUTES
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Author
HAS NO TEXT
-----------------------------
同样, Cost
事件返回如下:
EVENT TYPE(1):START_ELEMENT
HAS NAME: Cost
HAS NO TEXT
HAS ATTRIBUTES:
ATTRIBUTE-PREFIX:
ATTRIBUTE-NAMESP: null
ATTRIBUTE-NAME: currency
ATTRIBUTE-VALUE: USD
ATTRIBUTE-TYPE: CDATA
-----------------------------
EVENT TYPE(2):END_ELEMENT
HAS NAME: Cost
HAS NO TEXT
-----------------------------
有关 StAX 事件解析的更详细讨论,请参阅迭代器 API 和读取 XML 流。
运行筛选器示例
要编译并运行 Filter 示例,在终端窗口中,转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
Run the
MyStreamFilter
sample on theBookCatalogue.xml
file, with the following command. This example requires thejava.endorsed.dirs
system property to be set, to point to thesamples/lib
directory.java -Djava.endorsed.dirs=../lib stax/filter/MyStreamFilter -f stax/data/BookCatalogue.xml
MyStreamFilter
将打印出BookCatalogue.xml
文件定义的事件作为 XML 流。
读写示例
位于 INSTALL_DIR / jaxp-
版本 / samples / stax / readnwrite /
目录, EventProducerConsumer.java
演示如何同时将 StAX 解析器用作生产者和使用者。
StAX XMLEventWriter
API 扩展自XMLEventConsumer
接口,被称为事件消费者。相比之下, XMLEventReader
是事件生产者。 StAX 支持同时读取和写入,因此可以顺序读取一个 XML 流并同时写入另一个流。
Read-and-Write 示例显示了如何使用 StAX 生成器/使用者机制同时读写。此示例还显示了如何修改流以及如何动态添加新事件然后将其写入不同的流。
创建事件生产者/消费者
第一步是实例化一个事件工厂,然后创建一个事件生产者/消费者的实例:
XMLEventFactory m_eventFactory = XMLEventFactory.newInstance();
public EventProducerConsumer() {
// ...
try {
EventProducerConsumer ms = new EventProducerConsumer();
XMLEventReader reader = XMLInputFactory.newInstance().
createXMLEventReader(new java.io.FileInputStream(args[0]));
XMLEventWriter writer =
XMLOutputFactory.newInstance().createXMLEventWriter(System.out);
}
// ...
}
创建迭代器
下一步是创建一个迭代器来解析流:
while (reader.hasNext()) {
XMLEvent event = (XMLEvent)reader.next();
if (event.getEventType() == event.CHARACTERS) {
writer.add(ms.getNewCharactersEvent(event.asCharacters()));
}
else {
writer.add(event);
}
}
writer.flush();
创建一个作家
最后一步是以新的Character
事件的形式创建一个流编写器:
Characters getNewCharactersEvent(Characters event) {
if (event.getData().equalsIgnoreCase("Name1")) {
return m_eventFactory.createCharacters(
Calendar.getInstance().getTime().toString());
}
// else return the same event
else {
return event;
}
}
返回输出
当您运行读写示例时,将编译EventProducerConsumer
类,并将 XML 流解析为事件并写回STDOUT
。输出是示例 XML 文档中描述的BookCatalog.xml
文件的内容。
运行读写示例
要编译并运行读写示例,请在终端窗口中转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java
Run the
EventProducerConsumer
sample on theBookCatalogue.xml
file, with the following command.java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml
EventProducerConsumer
将打印出BookCatalogue.xml
文件的内容。
作家的例子
位于 INSTALL_DIR / jaxp-
版本 / samples / stax / writer /
目录, CursorWriter.java
演示了如何使用 StAX 游标 API 编写 XML 流。
创建输出工厂
第一步是创建XMLOutputFactory
的实例:
XMLOutputFactory xof = XMLOutputFactory.newInstance();
创建流编写器
下一步是创建XMLStreamWriter
的实例:
XMLStreamWriter xtw = null;
写流
最后一步是编写 XML 流。请注意,在写完最终EndDocument
后,将刷新并关闭流:
xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
xtw.writeComment("all elements here are explicitly in the HTML namespace");
xtw.writeStartDocument("utf-8","1.0");
xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40","html");
xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "head");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "title");
xtw.writeCharacters("Frobnostication");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "body");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "p");
xtw.writeCharacters("Moved to");
xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "a");
xtw.writeAttribute("href","http://frob.com");
xtw.writeCharacters("here");
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndElement();
xtw.writeEndDocument();
xtw.flush();
xtw.close();
返回输出
当您运行 Writer 示例时,将编译CursorWriter
类,并将 XML 流解析为事件并写入名为dist / CursorWriter-Output
的文件:
<!--all elements here are explicitly in the HTML namespace-->
<?xml version="1.0" encoding="utf-8"?>
<html:html xmlns:html="http://www.w3.org/TR/REC-html40">
<html:head>
<html:title>Frobnostication</html:title></html:head>
<html:body>
<html:p>Moved to <html:a href="http://frob.com">here</html:a>
</html:p>
</html:body>
</html:html>
在实际的dist / CursorWriter-Output
文件中,写入此流时没有任何换行符;这里添加了中断以使列表更易于阅读。在此示例中,与 Event 示例中的对象流一样,名称空间前缀也会添加到开始和结束 HTML 标记中。 StAX 规范不需要添加此前缀,但是当输出流的最终范围未明确知晓时,这是一种很好的做法。
运行 Writer 示例
要编译并运行 Writer 示例,在终端窗口中,转到 INSTALL_DIR
/ jaxp-
版本/ samples /
目录并键入以下内容:javac -classpath \
../lib/jaxp-ri.jar stax/writer/*.java
Run the
CursorWriter
sample, specifying the name of the file the output should be written to.java stax/writer/CursorWriter -f output_file
CursorWriter
将创建一个相应名称的输出文件,其中包含返回输出中显示的数据。