示例代码

原文: 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。在此示例中,过滤器仅接受StartElementEndElement事件,并过滤掉剩余的事件。

  • 读写示例readnwrite目录包含EventProducerConsumer.java ,它说明了如何使用 StAX 生成器/消费者机制同时读写 XML 流。

  • 编写器示例编写器目录包含CursorWriter.java ,它说明了如何使用XMLStreamWriter以编程方式编写 XML 文件。

除 Writer 示例之外的所有 StAX 示例都使用示例 XML 文档BookCatalog.xml

示例 XML 文档

大多数 StAX 示例类使用的示例 XML 文档BookCatalog.xml是一个基于常见BookCatalogue命名空间的简单书籍目录。 BookCatalog.xml的内容如下:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <BookCatalogue xmlns="http://www.publishing.org">
  3. <Book>
  4. <Title>Yogasana Vijnana: the Science of Yoga</Title>
  5. <author>Dhirendra Brahmachari</Author>
  6. <Date>1966</Date>
  7. <ISBN>81-40-34319-4</ISBN>
  8. <Publisher>Dhirendra Yoga Publications</Publisher>
  9. <Cost currency="INR">11.50</Cost>
  10. </Book>
  11. <Book>
  12. <Title>The First and Last Freedom</Title>
  13. <Author>J. Krishnamurti</Author>
  14. <Date>1954</Date>
  15. <ISBN>0-06-064831-7</ISBN>
  16. <Publisher>Harper &amp; Row</Publisher>
  17. <Cost currency="USD">2.95</Cost>
  18. </Book>
  19. </BookCatalogue>

游标示例

位于 INSTALL_DIR / jaxp- 版本 / samples / stax / cursor /目录, CursorParse.java演示使用 StAX 游标 API 读取 XML 文档。在 Cursor 示例中,应用程序通过调用next()指示解析器读取 XML 输入流中的下一个事件。

请注意, next()只返回一个整数常量,该常量对应于解析器所在的基础事件。应用程序需要调用相关函数以获取与基础事件相关的更多信息。

您可以将此方法想象为在 XML 输入流中移动的虚拟光标。当虚拟光标处于特定事件时,可以调用各种存取方法。

逐步完成活动

在此示例中,客户端应用程序通过在解析器上调用next方法来提取 XML 流中的下一个事件;例如:

  1. try {
  2. for (int i = 0 ; i < count ; i++) {
  3. // pass the file name.. all relative entity
  4. // references will be resolved against this
  5. // as base URI.
  6. XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
  7. new FileInputStream(filename));
  8. // when XMLStreamReader is created,
  9. // it is positioned at START_DOCUMENT event.
  10. int eventType = xmlr.getEventType();
  11. printEventType(eventType);
  12. printStartDocument(xmlr);
  13. // check if there are more events
  14. // in the input stream
  15. while(xmlr.hasNext()) {
  16. eventType = xmlr.next();
  17. printEventType(eventType);
  18. // these functions print the information
  19. // about the particular event by calling
  20. // the relevant function
  21. printStartElement(xmlr);
  22. printEndElement(xmlr);
  23. printText(xmlr);
  24. printPIData(xmlr);
  25. printComment(xmlr);
  26. }
  27. }
  28. }

注意, next只返回一个对应于当前光标位置的事件的整数常量。应用程序调用相关函数以获取与基础事件相关的更多信息。当光标处于特定事件时,可以调用各种存取方法。

返回字符串表示

因为next方法只返回与底层事件类型对应的整数,所以通常需要将这些整数映射到事件的字符串表示;例如:

  1. public final static String getEventTypeString(int eventType) {
  2. switch (eventType) {
  3. case XMLEvent.START_ELEMENT:
  4. return "START_ELEMENT";
  5. case XMLEvent.END_ELEMENT:
  6. return "END_ELEMENT";
  7. case XMLEvent.PROCESSING_INSTRUCTION:
  8. return "PROCESSING_INSTRUCTION";
  9. case XMLEvent.CHARACTERS:
  10. return "CHARACTERS";
  11. case XMLEvent.COMMENT:
  12. return "COMMENT";
  13. case XMLEvent.START_DOCUMENT:
  14. return "START_DOCUMENT";
  15. case XMLEvent.END_DOCUMENT:
  16. return "END_DOCUMENT";
  17. case XMLEvent.ENTITY_REFERENCE:
  18. return "ENTITY_REFERENCE";
  19. case XMLEvent.ATTRIBUTE:
  20. return "ATTRIBUTE";
  21. case XMLEvent.DTD:
  22. return "DTD";
  23. case XMLEvent.CDATA:
  24. return "CDATA";
  25. case XMLEvent.SPACE:
  26. return "SPACE";
  27. }
  28. return "UNKNOWN_EVENT_TYPE , " + eventType;
  29. }

运行游标示例

  1. 编译并运行光标示例,在终端窗口中,转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac stax/cursor/*.java
  2. Run the CursorParse sample on the BookCatalogue.xml file, with the following command.

    CursorParse将打印出BookCatalogue.xml文件的每个元素。

  1. 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

  1. XMLInputFactory xmlif = XMLInputFactory.newInstance();
  2. System.out.println("FACTORY: " + xmlif);
  3. xmlif.setEventAllocator(new XMLEventAllocatorImpl());
  4. allocator = xmlif.getEventAllocator();
  5. XMLStreamReader xmlr = xmlif.createXMLStreamReader(filename,
  6. new FileInputStream(filename));

创建事件迭代器

下一步是创建一个事件迭代器:

  1. int eventType = xmlr.getEventType();
  2. while (xmlr.hasNext()) {
  3. eventType = xmlr.next();
  4. // Get all "Book" elements as XMLEvent object
  5. if (eventType == XMLStreamConstants.START_ELEMENT
  6. && xmlr.getLocalName().equals("Book")) {
  7. // get immutable XMLEvent
  8. StartElement event = getXMLEvent(xmlr).asStartElement();
  9. System.out.println ("EVENT: " + event.toString());
  10. }
  11. }

创建分配器方法

最后一步是创建XMLEventAllocator方法:

  1. private static XMLEvent getXMLEvent(XMLStreamReader reader)
  2. throws XMLStreamException {
  3. return allocator.allocate(reader);
  4. }

运行光标到事件示例

  1. 要编译并运行游标到事件示例,在终端窗口中,转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac -classpath ../lib/jaxp-ri.jar stax/cursor2event/*.java
  2. Run the CursorApproachEventObject sample on the BookCatalogue.xml file, with the following command.

    1. java stax/cursor2event/CursorApproachEventObject stax/data/BookCatalogue.xml

    CursorApproachEventObject将打印出BookCatalogue.xml文件定义的事件列表。

事件示例

位于 INSTALL_DIR / jaxp- 版本 / samples / stax / event /目录, EventParse.java演示了如何使用 StAX 事件 API 来读取 XML 文档。

创建输入工厂

第一步是创建XMLInputFactory的新实例:

  1. XMLInputFactory factory = XMLInputFactory.newInstance();
  2. System.out.println("FACTORY: " + factory);

创建事件阅读器

下一步是创建XMLEventReader的实例:

  1. XMLEventReader r = factory.createXMLEventReader
  2. (filename, new FileInputStream(filename));

创建事件迭代器

第三步是创建一个事件迭代器:

  1. XMLEventReader r = factory.createXMLEventReader
  2. (filename, new FileInputStream(filename));
  3. while (r.hasNext()) {
  4. XMLEvent e = r.nextEvent();
  5. System.out.println(e.toString());
  6. }

获得活动流

最后一步是获取基础事件流:

  1. public final static String getEventTypeString(int eventType) {
  2. switch (eventType) {
  3. case XMLEvent.START_ELEMENT:
  4. return "START_ELEMENT";
  5. case XMLEvent.END_ELEMENT:
  6. return "END_ELEMENT";
  7. case XMLEvent.PROCESSING_INSTRUCTION:
  8. return "PROCESSING_INSTRUCTION";
  9. case XMLEvent.CHARACTERS:
  10. return "CHARACTERS";
  11. case XMLEvent.COMMENT:
  12. return "COMMENT";
  13. case XMLEvent.START_DOCUMENT:
  14. return "START_DOCUMENT";
  15. case XMLEvent.END_DOCUMENT:
  16. return "END_DOCUMENT";
  17. case XMLEvent.ENTITY_REFERENCE:
  18. return "ENTITY_REFERENCE";
  19. case XMLEvent.ATTRIBUTE:
  20. return "ATTRIBUTE";
  21. case XMLEvent.DTD:
  22. return "DTD";
  23. case XMLEvent.CDATA:
  24. return "CDATA";
  25. case XMLEvent.SPACE:
  26. return "SPACE";
  27. }
  28. return "UNKNOWN_EVENT_TYPE," + eventType;
  29. }

返回输出

当您运行 Event 示例时,将编译EventParse类,并将 XML 流解析为事件并返回到STDOUT 。例如,作者元素的实例返回为:

  1. <[’http://www.publishing.org’]::Author>
  2. Dhirendra Brahmachari
  3. </[’http://www.publishing.org’]::Author>

请注意,在此示例中,事件包含一个开始和结束标记,两者都包含命名空间。元素的内容在标记内作为字符串返回。

类似地, Cost元素的实例返回为:

  1. <[’http://www.publishing.org’]::Cost currency=’INR’>
  2. 11.50
  3. </[’http://www.publishing.org’]::Cost

在这种情况下,货币属性和值将在事件的开始标记中返回。

运行事件示例

  1. 要编译并运行事件示例,在终端窗口中,转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
  2. Run the EventParse sample on the BookCatalogue.xml file, with the following command.

    1. java stax/event/EventParse stax/data/BookCatalogue.xml

    EventParse将打印出来自BookCatalogue.xml文件定义的所有元素的数据。

过滤器示例

位于 INSTALL_DIR / jaxp- 版本 / samples / stax / filter /目录, MyStreamFilter.java演示了如何使用 StAX 流过滤器 API 过滤掉应用程序不需要的事件。在此示例中,解析器筛选出除StartElementEndElement之外的所有事件。

实现 StreamFilter 类

MyStreamFilter类实现javax.xml.stream.StreamFilter

  1. public class MyStreamFilter implements javax.xml.stream.StreamFilter {
  2. // ...
  3. }

创建输入工厂

下一步是创建XMLInputFactory的实例。在这种情况下,工厂也会设置各种属性:

  1. XMLInputFactory xmlif = null ;
  2. try {
  3. xmlif = XMLInputFactory.newInstance();
  4. xmlif.setProperty(
  5. XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
  6. Boolean.TRUE);
  7. xmlif.setProperty(
  8. XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
  9. Boolean.FALSE);
  10. xmlif.setProperty(
  11. XMLInputFactory.IS_NAMESPACE_AWARE,
  12. Boolean.TRUE);
  13. xmlif.setProperty(
  14. XMLInputFactory.IS_COALESCING,
  15. Boolean.TRUE);
  16. }
  17. catch (Exception ex) {
  18. ex.printStackTrace();
  19. }
  20. System.out.println("FACTORY: " + xmlif);
  21. System.out.println("filename = "+ filename);

创建过滤器

下一步是实例化文件输入流并创建流过滤器:

  1. FileInputStream fis = new FileInputStream(filename);
  2. XMLStreamReader xmlr = xmlif.createFilteredReader(
  3. xmlif.createXMLStreamReader(fis),
  4. new MyStreamFilter());
  5. int eventType = xmlr.getEventType();
  6. printEventType(eventType);
  7. while (xmlr.hasNext()) {
  8. eventType = xmlr.next();
  9. printEventType(eventType);
  10. printName(xmlr,eventType);
  11. printText(xmlr);
  12. if (xmlr.isStartElement()) {
  13. printAttributes(xmlr);
  14. }
  15. printPIData(xmlr);
  16. System.out.println("-----------------------");
  17. }

捕获事件流

下一步是捕获事件流。这基本上与事件示例中的方式相同。

过滤流

最后一步是过滤流:

  1. public boolean accept(XMLStreamReader reader) {
  2. if (!reader.isStartElement() && !reader.isEndElement())
  3. return false;
  4. else
  5. return true;
  6. }

返回输出

当您运行 Filter 示例时,将编译MyStreamFilter类,并将 XML 流解析为事件并返回到STDOUT 。例如,作者事件返回如下:

  1. EVENT TYPE(1):START_ELEMENT
  2. HAS NAME: Author
  3. HAS NO TEXT
  4. HAS NO ATTRIBUTES
  5. -----------------------------
  6. EVENT TYPE(2):END_ELEMENT
  7. HAS NAME: Author
  8. HAS NO TEXT
  9. -----------------------------

同样, Cost事件返回如下:

  1. EVENT TYPE(1):START_ELEMENT
  2. HAS NAME: Cost
  3. HAS NO TEXT
  4. HAS ATTRIBUTES:
  5. ATTRIBUTE-PREFIX:
  6. ATTRIBUTE-NAMESP: null
  7. ATTRIBUTE-NAME: currency
  8. ATTRIBUTE-VALUE: USD
  9. ATTRIBUTE-TYPE: CDATA
  10. -----------------------------
  11. EVENT TYPE(2):END_ELEMENT
  12. HAS NAME: Cost
  13. HAS NO TEXT
  14. -----------------------------

有关 StAX 事件解析的更详细讨论,请参阅迭代器 API读取 XML 流

运行筛选器示例

  1. 要编译并运行 Filter 示例,在终端窗口中,转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
  2. Run the MyStreamFilter sample on the BookCatalogue.xml file, with the following command. This example requires the java.endorsed.dirs system property to be set, to point to the samples/lib directory.

    1. 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 生成器/使用者机制同时读写。此示例还显示了如何修改流以及如何动态添加新事件然后将其写入不同的流。

创建事件生产者/消费者

第一步是实例化一个事件工厂,然后创建一个事件生产者/消费者的实例:

  1. XMLEventFactory m_eventFactory = XMLEventFactory.newInstance();
  2. public EventProducerConsumer() {
  3. // ...
  4. try {
  5. EventProducerConsumer ms = new EventProducerConsumer();
  6. XMLEventReader reader = XMLInputFactory.newInstance().
  7. createXMLEventReader(new java.io.FileInputStream(args[0]));
  8. XMLEventWriter writer =
  9. XMLOutputFactory.newInstance().createXMLEventWriter(System.out);
  10. }
  11. // ...
  12. }

创建迭代器

下一步是创建一个迭代器来解析流:

  1. while (reader.hasNext()) {
  2. XMLEvent event = (XMLEvent)reader.next();
  3. if (event.getEventType() == event.CHARACTERS) {
  4. writer.add(ms.getNewCharactersEvent(event.asCharacters()));
  5. }
  6. else {
  7. writer.add(event);
  8. }
  9. }
  10. writer.flush();

创建一个作家

最后一步是以新的Character事件的形式创建一个流编写器:

  1. Characters getNewCharactersEvent(Characters event) {
  2. if (event.getData().equalsIgnoreCase("Name1")) {
  3. return m_eventFactory.createCharacters(
  4. Calendar.getInstance().getTime().toString());
  5. }
  6. // else return the same event
  7. else {
  8. return event;
  9. }
  10. }

返回输出

当您运行读写示例时,将编译EventProducerConsumer类,并将 XML 流解析为事件并写回STDOUT 。输出是示例 XML 文档中描述的BookCatalog.xml文件的内容。

运行读写示例

  1. 要编译并运行读写示例,请在终端窗口中转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java
  2. Run the EventProducerConsumer sample on the BookCatalogue.xml file, with the following command.

    1. java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml

    EventProducerConsumer将打印出BookCatalogue.xml文件的内容。

作家的例子

位于 INSTALL_DIR / jaxp- 版本 / samples / stax / writer /目录, CursorWriter.java演示了如何使用 StAX 游标 API 编写 XML 流。

创建输出工厂

第一步是创建XMLOutputFactory的实例:

  1. XMLOutputFactory xof = XMLOutputFactory.newInstance();

创建流编写器

下一步是创建XMLStreamWriter的实例:

  1. XMLStreamWriter xtw = null;

写流

最后一步是编写 XML 流。请注意,在写完最终EndDocument后,将刷新并关闭流:

  1. xtw = xof.createXMLStreamWriter(new FileWriter(fileName));
  2. xtw.writeComment("all elements here are explicitly in the HTML namespace");
  3. xtw.writeStartDocument("utf-8","1.0");
  4. xtw.setPrefix("html", "http://www.w3.org/TR/REC-html40");
  5. xtw.writeStartElement("http://www.w3.org/TR/REC-html40","html");
  6. xtw.writeNamespace("html", "http://www.w3.org/TR/REC-html40");
  7. xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "head");
  8. xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "title");
  9. xtw.writeCharacters("Frobnostication");
  10. xtw.writeEndElement();
  11. xtw.writeEndElement();
  12. xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "body");
  13. xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "p");
  14. xtw.writeCharacters("Moved to");
  15. xtw.writeStartElement("http://www.w3.org/TR/REC-html40", "a");
  16. xtw.writeAttribute("href","http://frob.com");
  17. xtw.writeCharacters("here");
  18. xtw.writeEndElement();
  19. xtw.writeEndElement();
  20. xtw.writeEndElement();
  21. xtw.writeEndElement();
  22. xtw.writeEndDocument();
  23. xtw.flush();
  24. xtw.close();

返回输出

当您运行 Writer 示例时,将编译CursorWriter类,并将 XML 流解析为事件并写入名为dist / CursorWriter-Output的文件:

  1. <!--all elements here are explicitly in the HTML namespace-->
  2. <?xml version="1.0" encoding="utf-8"?>
  3. <html:html xmlns:html="http://www.w3.org/TR/REC-html40">
  4. <html:head>
  5. <html:title>Frobnostication</html:title></html:head>
  6. <html:body>
  7. <html:p>Moved to <html:a href="http://frob.com">here</html:a>
  8. </html:p>
  9. </html:body>
  10. </html:html>

在实际的dist / CursorWriter-Output文件中,写入此流时没有任何换行符;这里添加了中断以使列表更易于阅读。在此示例中,与 Event 示例中的对象流一样,名称空间前缀也会添加到开始和结束 HTML 标记中。 StAX 规范不需要添加此前缀,但是当输出流的最终范围未明确知晓时,这是一种很好的做法。

运行 Writer 示例

  1. 要编译并运行 Writer 示例,在终端窗口中,转到 INSTALL_DIR / jaxp- 版本 / samples /目录并键入以下内容:

    1. javac -classpath \
    2. ../lib/jaxp-ri.jar stax/writer/*.java
  2. Run the CursorWriter sample, specifying the name of the file the output should be written to.

    1. java stax/writer/CursorWriter -f output_file

    CursorWriter将创建一个相应名称的输出文件,其中包含返回输出中显示的数据。