# 使用 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` 方法用于转义`&等字符。` ,`<` ,`>` 和`“`。绑定前缀可以通过传递前缀的实际值,使用 `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(新行是非规范的): ``` Jean Arp ``` ### 使用 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` 事件。 您可以使用`字符`方法转义`&` ,`<` ,`>` 和`“`。 `setPrefix(...)`方法可用于显式绑定输出期间使用的前缀, `getPrefix(...)`方法可用于获取当前前缀。请注意,默认情况下, `XMLEventWriter` 会将命名空间绑定添加到其内部命名空间映射中。对于绑定它们的事件,前缀超出了相应的 `EndElement` 之后的范围。