# 示例代码

> 原文： [https://docs.oracle.com/javase/tutorial/jaxp/stax/example.html](https://docs.oracle.com/javase/tutorial/jaxp/stax/example.html)

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

本节涉及的主题如下：

*   [示例代码组织](#bnbfm)

*   [示例 XML 文档](#bnbfn)

*   [光标示例](#bnbfo)

*   [光标到事件示例](#bnbft)

*   [事件示例](#bnbfz)

*   [过滤器示例](#bnbgh)

*   [读写示例](#bnbgq)

*   [作家示例](#bnbgx)

## 示例代码组织

_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 &amp; 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;
}

```

### 运行游标示例

1.  **编译并运行光标示例，在终端窗口中，转到 _INSTALL_DIR_ `/ jaxp-` _ 版本 _ `/ samples /` 目录并键入以下内容：**

    ```
    javac stax/cursor/*.java

    ```

2.  **Run the `CursorParse` sample on the `BookCatalogue.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);
}

```

### 运行光标到事件示例

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

    ```
    javac -classpath ../lib/jaxp-ri.jar stax/cursor2event/*.java

    ```

2.  **Run the `CursorApproachEventObject` sample on the `BookCatalogue.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

```

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

### 运行事件示例

1.  **要编译并运行事件示例，在终端窗口中，转到 _INSTALL_DIR_ `/ jaxp-` _ 版本 _ `/ samples /` 目录并键入以下内容：**

    ```
    javac -classpath ../lib/jaxp-ri.jar stax/event/*.java

    ```

2.  **Run the `EventParse` sample on the `BookCatalogue.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](api.html#bnbee) 和[读取 XML 流](using.html#bnbew)。

### 运行筛选器示例

1.  **要编译并运行 Filter 示例，在终端窗口中，转到 _INSTALL_DIR_ `/ jaxp-` _ 版本 _ `/ samples /` 目录并键入以下内容：**

    ```
    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.**

    ```
    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 文档](#bnbfn)中描述的 `BookCatalog.xml` 文件的内容。

### 运行读写示例

1.  **要编译并运行读写示例，请在终端窗口中转到 _INSTALL_DIR_ `/ jaxp-` _ 版本 _ `/ samples /` 目录并键入以下内容：**

    ```
    javac -classpath ../lib/jaxp-ri.jar stax/readnwrite/*.java

    ```

2.  **Run the `EventProducerConsumer` sample on the `BookCatalogue.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 示例

1.  **要编译并运行 Writer 示例，在终端窗口中，转到 _INSTALL_DIR_ `/ jaxp-` _ 版本 _ `/ samples /` 目录并键入以下内容：**

    ```
    javac -classpath \
        ../lib/jaxp-ri.jar stax/writer/*.java

    ```

2.  **Run the `CursorWriter` sample, specifying the name of the file the output should be written to.**

    ```
    java stax/writer/CursorWriter -f output_file

    ```

    `CursorWriter` 将创建一个相应名称的输出文件，其中包含[返回输出](#bnbhb)中显示的数据。