Example Code

本节将逐步介绍 JAXP 参考实现 Binding 包中包含的示例 StAX 代码。本节中使用的所有示例目录都位于 INSTALL_DIR /jaxp\-版本/samples/stax目录中。

本节涵盖的主题如下:

示例代码组织

INSTALL_DIR /jaxp\-版本/samples/stax目录包含六个 StAX 示例目录:

  • 游标示例cursor目录包含CursorParse\.java,它说明了如何使用XMLStreamReader(游标)API 来读取 XML 文件。

  • 从游标到事件的示例cursor2event目录包含CursorApproachEventObject\.java,它说明了使用游标 API 时应用程序如何获取信息作为XMLEvent对象。

  • 事件示例event目录包含EventParse\.java,它说明了如何使用XMLEventReader(事件迭代器)API 读取 XML 文件。

  • 过滤器示例filter目录包含MyStreamFilter\.java,它说明了如何使用 StAX 流过滤器 API。在此示例中,过滤器仅接受StartElementEndElement事件,并过滤掉其余事件。

  • 读写示例readnwrite目录包含EventProducerConsumer\.java,它说明了如何使用 StAX 生产者/Consumer 机制同时读取和写入 XML 流。

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

除 Writer 示 exception,所有 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 &amp; Row</Publisher>
    <Cost currency="USD">2.95</Cost>
</Book>
</BookCatalogue>

Cursor Example

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

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

您可以将这种方法想象为在 XMLImporting 流中移动的虚拟游标。当该虚拟光标处于特定事件时,可以调用多种访问器方法。

逐步完成活动

在此示例中,Client 端应用程序通过在解析器上调用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方法仅返回与基础事件类型相对应的整数,因此通常需要将这些整数 Map 到事件的字符串 表示形式;例如:

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
  • 使用以下命令在BookCatalogue\.xml文件上运行CursorParse示例.

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

java stax/event/CursorParse stax/data/BookCatalogue.xml

Cursor-to-Event Example

CursorApproachEventObject\.java位于 tut-install /javaeetutorial5/examples/stax/cursor2event/目录中,演示了如何获取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
  • 使用以下命令在BookCatalogue\.xml文件上运行CursorApproachEventObject示例.
java stax/cursor2event/CursorApproachEventObject stax/data/BookCatalogue.xml

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

Event Example

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

创建 Importing 工厂

第一步是创建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。例如,Author元素的实例返回为:

<['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

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

运行事件示例

  • 要编译并运行事件示例,请在终端窗口中,转到 INSTALL_DIR /jaxp\-版本/samples/目录并键入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/event/*.java
  • 使用以下命令在BookCatalogue\.xml文件上运行EventParse示例.
java stax/event/EventParse stax/data/BookCatalogue.xml

EventParse将打印BookCatalogue\.xml文件定义的所有元素中的数据。

Filter Example

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

实现 StreamFilter 类

MyStreamFilter类实现javax\.xml\.stream\.StreamFilter

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

创建 Importing 工厂

下一步是创建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);

创建过滤器

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

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("-----------------------");
}

catch事件流

下一步是catch事件流。基本上以与事件示例相同的方式进行。

过滤流

最后一步是过滤流:

public boolean accept(XMLStreamReader reader) {
    if (!reader.isStartElement() && !reader.isEndElement())
        return false;
    else
        return true;
}

返回输出

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

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 事件解析的详细讨论,请参见Iterator API读取 XML 流

运行过滤器示例

  • 要编译并运行“筛选器”示例,请在终端窗口中,转到 INSTALL_DIR /jaxp\-版本/samples/目录并键入以下内容:
javac -classpath ../lib/jaxp-ri.jar stax/filter/*.java
  • 使用以下命令在BookCatalogue\.xml文件上运行MyStreamFilter示例.本示例要求设置java\.endorsed\.dirs系统属性,以指向samples/lib目录.
java -Djava.endorsed.dirs=../lib stax/filter/MyStreamFilter -f stax/data/BookCatalogue.xml

MyStreamFilter将把BookCatalogue\.xml文件定义的事件打印为 XML 流。

Read-and-Write Example

EventProducerConsumer\.java位于 INSTALL_DIR /jaxp\-版本/samples/stax/readnwrite/目录中,演示了如何同时作为生产者和使用者使用 StAX 解析器。

StAX XMLEventWriter API 从XMLEventConsumerinterface扩展而来,称为“事件使用者”。相比之下,XMLEventReader是“事件产生器” **。 StAX 支持同时读取和写入,因此可以 Sequences 地从一个 XML 流读取并同时写入另一个流。

读写示例显示了如何使用 StAX 生产者/Consumer 机制同时读取和写入。此示例还显示了如何修改流以及如何动态添加新事件然后将其写入不同的流。

创建事件生产者/Consumer

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

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();

创建 Writer

最后一步是以新的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
  • 使用以下命令在BookCatalogue\.xml文件上运行EventProducerConsumer示例.
java stax/readnwrite/EventProducerConsumer stax/data/BookCatalogue.xml

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

Writer Example

CursorWriter\.java位于 INSTALL_DIR /jaxp\-版本/samples/stax/writer/目录中,演示了如何使用 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文件中,写入此流时没有任何换行符;此处已添加中断,以使清单更易于阅读。在此示例中,与事件示例中的对象流一样,将名称空间前缀添加到开始和结束 HTML 标签中。 StAX 规范不需要添加此前缀,但是当不确定输出流的final范围时,这是一种好习惯。

运行 Writer 示例

  • 要编译并运行 Writer 示例,请在终端窗口中,转到 INSTALL_DIR /jaxp\-版本/samples/目录并键入以下内容:
javac -classpath \
    ../lib/jaxp-ri.jar stax/writer/*.java
  • 运行CursorWriter示例,指定输出应写入的文件名.
java stax/writer/CursorWriter -f output_file

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