# 使用 StAX

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

通常，StAX 程序员使用 `XMLInputFactory` ， `XMLOutputFactory` 和 `XMLEventFactory` 类创建 XML 流读取器，编写器和事件。通过在工厂上设置属性来完成配置，从而可以使用工厂上的 `setProperty` 方法将特定于实现的设置传递给底层实现。同样，可以使用 `getProperty` 工厂方法查询特定于实现的设置。

下面介绍 `XMLInputFactory` ， `XMLOutputFactory` 和 `XMLEventFactory` 类，然后讨论资源分配，命名空间和属性管理，错误处理，最后阅读和使用游标和迭代器 API 编写流。

## StAX 工厂类

StAX 工厂类。 `XMLInputFactory` ， `XMLOutputFactory` 和 `XMLEventFactory` ，让您定义和配置 XML 流阅读器，流编写器和事件类的实现实例。

### 的 XMLInputFactory

`XMLInputFactory` 类允许您配置工厂创建的 XML 流读取器处理器的实现实例。抽象类 `XMLInputFactory` 的新实例是通过调用类上的 `newInstance` 方法创建的。然后使用静态方法 `XMLInputFactory.newInstance` 创建新的工厂实例。

从[JGP]派生， `XMLInputFactory.newInstance` 方法通过使用以下查找过程确定要加载的特定 `XMLInputFactory` 实现类：

1.  使用 `javax.xml.stream.XMLInputFactory` 系统属性。

2.  使用 Java SE 平台的 Java 运行时环境（JRE）目录中的 `lib / xml.stream.properties` 文件。

3.  使用 Services API（如果可用）通过查看 JRE 可用的 JAR 文件中的 `META-INF / services / javax.xml.stream.XMLInputFactory` 文件来确定类名。

4.  使用平台默认 `XMLInputFactory` 实例。

在获得对适当的 `XMLInputFactory` 的引用之后，应用程序可以使用工厂来配置和创建流实例。下表列出了 `XMLInputFactory` 支持的属性。有关更详细的列表，请参阅 StAX 规范。

`javax.xml.stream.XMLInputFactory` Properties 
 

| 属性 | 描述 |
| :-- | :-- |
| `isValidating` | 打开特定于实现的验证。 |
| `是合并` | **（必需）**要求处理器合并相邻的字符数据。 |
| `isNamespaceAware` | 关闭名称空间支持。所有实现都必须支持名称空间。支持非命名空间感知文档是可选的。 |
| `isReplacingEntityReferences` | **（必需）**要求处理器用其替换值替换内部实体引用，并将它们报告为描述实体的字符或事件集。 |
| `isSupportingExternalEntities` | **（必填）**要求处理器解析外部解析的实体。 |
| `记者` | **（必需）**设置并获取 `XMLReporter` 接口的实现。 |
| `旋转变压器` | **（必需）**设置并获取 `XMLResolver` 接口的实现。 |
| `分配器` | **（必需）**设置并获取 `XMLEventAllocator` 接口的实现。 |

### XMLOutputFactory

抽象类 `XMLOutputFactory` 的新实例是通过调用类上的 `newInstance` 方法创建的。然后使用静态方法 `XMLOutputFactory.newInstance` 创建新的工厂实例。用于获取实例的算法与 `XMLInputFactory` 相同，但引用 `javax.xml.stream.XMLOutputFactory` 系统属性。

`XMLOutputFactory` 仅支持一个属性， `javax.xml.stream.isRepairingNamespaces` 。此属性是必需的，其目的是创建默认前缀并将它们与名称空间 URI 相关联。有关更多信息，请参阅 StAX 规范。

### XMLEventFactory

抽象类 `XMLEventFactory` 的新实例是通过调用类上的 `newInstance` 方法创建的。然后使用静态方法 `XMLEventFactory.newInstance` 创建新的工厂实例。该工厂引用 `javax.xml.stream.XMLEventFactory` 属性来实例化工厂。用于获取实例的算法与 `XMLInputFactory` 和 `XMLOutputFactory` 相同，但引用 `javax.xml.stream.XMLEventFactory` 系统属性。

`XMLEventFactory` 没有默认属性。

## 资源，命名空间和错误

StAX 规范处理资源解析，属性和命名空间以及错误和异常，如下所述。

### 资源分辨率

`XMLResolver` 接口提供了一种设置在 XML 处理期间解析资源的方法的方法。应用程序在 `XMLInputFactory` 上设置接口，然后在该工厂实例创建的所有处理器上设置接口。

### 属性和命名空间

属性由 StAX 处理器使用游标界面中的查找方法和字符串以及迭代器接口中的`属性`和`命名空间`事件进行报告。请注意，命名空间被视为属性，尽管命名空间与游标和迭代器 API 中的属性分开报告。另请注意，对于 StAX 处理器，名称空间处理是可选的。有关命名空间绑定和可选命名空间处理的完整信息，请参阅 StAX 规范。

### 错误报告和异常处理

通过 `javax.xml.stream.XMLStreamException` 接口报告所有致命错误。使用 `javax.xml.stream.XMLReporter` 接口报告所有非致命错误和警告。

## 阅读 XML 流

如本课程前面所述，使用 StAX 处理器读取 XML 流的方式 - 更重要的是，您获得的内容 - 根据您使用的是 StAX 游标 API 还是事件迭代器 API 而有很大差异。以下两节介绍如何使用这些 API 读取 XML 流。

### 使用 XMLStreamReader

StAX 游标 API 中的 `XMLStreamReader` 接口允许您仅向前读取 XML 流或文档，一次只能在信息集中读取一个项目。以下方法可用于从流中提取数据或跳过不需要的事件：

*   获取属性的值

*   阅读 XML 内容

*   确定元素是否包含内容或为空

*   获取对属性集合的索引访问权限

*   获取对命名空间集合的索引访问权限

*   获取当前事件的名称（如果适用）

*   获取当前事件的内容（如果适用）

`XMLStreamReader` 的实例在任何时候都有一个当前事件，其方法在其上运行。当您在流上创建 `XMLStreamReader` 的实例时，初始当前事件是 `START_DOCUMENT` 状态。然后可以使用 `XMLStreamReader.next` 方法步进到流中的下一个事件。

### 读取属性，属性和命名空间

`XMLStreamReader.next` 方法加载流中下一个事件的属性。然后，您可以通过调用 `XMLStreamReader.getLocalName` 和 `XMLStreamReader.getText` 方法来访问这些属性。

当 `XMLStreamReader` 游标位于 `StartElement` 事件上时，它会读取事件的名称和任何属性，包括命名空间。可以使用索引值访问事件的所有属性，也可以通过名称空间 URI 和本地名称查找。但请注意，只有当前 `StartEvent` 上声明的命名空间可用;不维护以前声明的名称空间，并且不会删除重新声明的名称空间。

### XMLStreamReader 方法

`XMLStreamReader` 提供以下方法来检索有关命名空间和属性的信息：

```
int getAttributeCount();
String getAttributeNamespace(int index);
String getAttributeLocalName(int index);
String getAttributePrefix(int index);
String getAttributeType(int index);
String getAttributeValue(int index);
String getAttributeValue(String namespaceUri, String localName);
boolean isAttributeSpecified(int index);

```

也可以使用另外三种方法访问命名空间：

```
int getNamespaceCount();
String getNamespacePrefix(int index);
String getNamespaceURI(int index);

```

### 实例化 XMLStreamReader

此示例取自 StAX 规范，显示了如何实例化输入工厂，创建读取器以及迭代 XML 流的元素：

```
XMLInputFactory f = XMLInputFactory.newInstance();
XMLStreamReader r = f.createXMLStreamReader( ... );
while(r.hasNext()) {
    r.next();
}

```

### 使用 XMLEventReader

StAX 事件迭代器 API 中的 `XMLEventReader` API 提供了将 XML 流中的事件映射到可以自由重用的已分配事件对象的方法，并且可以扩展 API 本身来处理自定义事件。

`XMLEventReader` 提供了四种迭代解析 XML 流的方法：

*   `next` ：返回流中的下一个事件

*   `nextEvent` ：返回下一个键入的 XMLEvent

*   `hasNext` ：如果要在流中处理更多事件，则返回 true

*   `peek` ：返回事件但不迭代到下一个事件

例如，以下代码片段说明了 `XMLEventReader` 方法声明：

```
package javax.xml.stream;
import java.util.Iterator;

public interface XMLEventReader extends Iterator {
    public Object next();
    public XMLEvent nextEvent() throws XMLStreamException;
    public boolean hasNext();
    public XMLEvent peek() throws XMLStreamException;
    // ...
}

```

要读取流上的所有事件然后打印它们，您可以使用以下内容：

```
while(stream.hasNext()) {
    XMLEvent event = stream.nextEvent();
    System.out.print(event);
}

```

### 阅读属性

您可以从关联的 `javax.xml.stream.StartElement` 访问属性，如下所示：

```
public interface StartElement extends XMLEvent {
    public Attribute getAttributeByName(QName name);
    public Iterator getAttributes();
}

```

您可以使用 `StartElement` 接口上的 `getAttributes` 方法，在 `StartElement` 上声明的所有属性上使用 `Iterator` 。

### 读取命名空间

与读取属性类似，使用通过调用 `StartElement` 接口上的 `getNamespaces` 方法创建的`迭代器`来读取命名空间。仅返回当前 `StartElement` 的命名空间，并且应用程序可以使用 `StartElement.getNamespaceContext` 获取当前命名空间上下文。

## 编写 XML 流

StAX 是一个双向 API，游标和事件迭代器 API 都有自己的一组用于编写 XML 流的接口。与用于读取流的接口一样，游标和事件迭代器的编写器 API 之间存在显着差异。以下部分描述了如何使用这些 API 编写 XML 流。

### 使用 XMLStreamWriter

StAX 游标 API 中的 `XMLStreamWriter` 接口允许应用程序写回 XML 流或创建全新的流。 XMLStreamWriter 有一些方法可以让你：

*   编写格式良好的 XML

*   冲洗或关闭输出

*   写限定名称

请注意， `XMLStreamWriter` 实现不需要对输入执行格式良好或有效性检查。虽然某些实现可能会执行严格的错误检查，但其您实现的规则适用于 `XMLOutputFactory` 类中定义的属性。

`writeCharacters` 方法用于转义`&amp;等字符。` ，`&lt;` ，`&gt;` 和`“`。绑定前缀可以通过传递前缀的实际值，使用 `setPrefix` 方法，或通过设置默认名称空间声明的属性来处理。

以下示例取自 StAX 规范，演示了如何实例化输出工厂，创建编写器以及编写 XML 输出：

```
XMLOutputFactory output = XMLOutputFactory.newInstance();
XMLStreamWriter writer = output.createXMLStreamWriter( ... );

writer.writeStartDocument();
writer.setPrefix("c","http://c");
writer.setDefaultNamespace("http://c");

writer.writeStartElement("http://c","a");
writer.writeAttribute("b","blah");
writer.writeNamespace("c","http://c");
writer.writeDefaultNamespace("http://c");

writer.setPrefix("d","http://c");
writer.writeEmptyElement("http://c","d");
writer.writeAttribute("http://c", "chris","fry");
writer.writeNamespace("d","http://c");
writer.writeCharacters("Jean Arp");
writer.writeEndElement();

writer.flush();

```

此代码生成以下 XML（新行是非规范的）：

```
<?xml version=’1.0’ encoding=’utf-8’?>
<a b="blah" xmlns:c="http://c" xmlns="http://c">
<d:d d:chris="fry" xmlns:d="http://c"/>Jean Arp</a>

```

### 使用 XMLEventWriter

StAX 事件迭代器 API 中的 `XMLEventWriter` 接口允许应用程序写回 XML 流或创建全新的流。此 API 可以扩展，但主要 API 如下：

```
public interface XMLEventWriter {
    public void flush() throws XMLStreamException;
    public void close() throws XMLStreamException;
    public void add(XMLEvent e) throws XMLStreamException;
    // ... other methods not shown.
}

```

`XMLEventWriter` 的实例由 `XMLOutputFactory` 的实例创建。迭代地添加流事件，并且在将事件添加到事件编写器实例之后不能修改事件。

### 属性，转义字符，绑定前缀

需要 StAX 实现来缓冲最后一个 `StartElement` ，直到在流中添加或遇到`属性`或`命名空间`以外的事件。这意味着当您将`属性`或`命名空间`添加到流时，它会附加到当前 `StartElement` 事件。

您可以使用`字符`方法转义`&amp;` ，`&lt;` ，`&gt;` 和`“`。

`setPrefix（...）`方法可用于显式绑定输出期间使用的前缀， `getPrefix（...）`方法可用于获取当前前缀。请注意，默认情况下， `XMLEventWriter` 会将命名空间绑定添加到其内部命名空间映射中。对于绑定它们的事件，前缀超出了相应的 `EndElement` 之后的范围。