StAX API

原文: https://docs.oracle.com/javase/tutorial/jaxp/stax/api.html

StAX API 公开了用于迭代,基于事件的 XML 文档处理的方法。 XML 文档被视为过滤的一系列事件,并且信息集状态可以以过程方式存储。此外,与 SAX 不同,StAX API 是双向的,可以读取和写入 XML 文档。

StAX API 实际上是两个不同的 API 集:游标 API 和迭代器 API。本课程后面将更详细地介绍这两个 API 集,但下面将简要介绍它们的主要功能。

游标 API

顾名思义,StAX 游标 API 代表一个游标,您可以使用该游标从头到尾遍历 XML 文档。这个光标可以一次指向一个东西,并且始终向前移动,从不向后移动,通常一次移动一个信息集元素。

两个主要的游标接口是XMLStreamReaderXMLStreamWriterXMLStreamReader包括可从 XML 信息模型检索的所有可能信息的访问器方法,包括文档编码,元素名称,属性,命名空间,文本节点,开始标记,注释,处理指令,文档边界等等;例如:

  1. public interface XMLStreamReader {
  2. public int next() throws XMLStreamException;
  3. public boolean hasNext() throws XMLStreamException;
  4. public String getText();
  5. public String getLocalName();
  6. public String getNamespaceURI();
  7. // ... other methods not shown
  8. }

您可以在XMLStreamReader上调用方法,例如getTextgetName ,以获取当前光标位置的数据。 XMLStreamWriter提供与StartElementEndElement事件类型相对应的方法;例如:

  1. public interface XMLStreamWriter {
  2. public void writeStartElement(String localName) throws XMLStreamException;
  3. public void writeEndElement() throws XMLStreamException;
  4. public void writeCharacters(String text) throws XMLStreamException;
  5. // ... other methods not shown
  6. }

游标 API 以多种方式镜像 SAX。例如,方法可用于直接访问字符串和字符信息,整数索引可用于访问属性和名称空间信息。与 SAX 一样,游标 API 方法将 XML 信息作为字符串返回,从而最大限度地减少了对象分配要求。

迭代器 API

StAX 迭代器 API 将 XML 文档流表示为一组离散事件对象。这些事件由应用程序提取,并由解析器按照在源 XML 文档中读取它们的顺序提供。

基本迭代器接口称为XMLEventXMLEvent 表中列出的每种事件类型都有子接口。用于读取迭代器事件的主要解析器接口是XMLEventReader ,用于编写迭代器事件的主要接口是XMLEventWriterXMLEventReader接口包含五种方法,其中最重要的是nextEvent ,它返回 XML 流中的下一个事件。 XMLEventReader实现了java.util.Iterator ,这意味着来自XMLEventReader的返回可以被缓存或传递到可以与标准 Java Iterator 一起使用的例程中;例如:

  1. public interface XMLEventReader extends Iterator {
  2. public XMLEvent nextEvent() throws XMLStreamException;
  3. public boolean hasNext();
  4. public XMLEvent peek() throws XMLStreamException;
  5. // ...
  6. }

类似地,在迭代器 API 的输出端,您有:

  1. public interface XMLEventWriter {
  2. public void flush() throws XMLStreamException;
  3. public void close() throws XMLStreamException;
  4. public void add(XMLEvent e) throws XMLStreamException;
  5. public void add(Attribute attribute) throws XMLStreamException;
  6. // ...
  7. }

迭代器事件类型

XMLEvent Types Defined in the Event Iterator API

事件类型 描述
StartDocument 报告一组 XML 事件的开始,包括编码,XML 版本和独立属性。
StartElement 报告元素的开始,包括任何属性和名称空间声明;还可以访问开始标记的前缀,名称空间 URI 和本地名称。
EndElement 报告元素的结束标记。如果已在相应的StartElement上明确设置了超出范围的命名空间,则可以在此处调用它们。
Character 对应于 XML CData部分和CharacterData实体。请注意,可忽略的空白区域和显着的空白区域也会报告为Character事件。
EntityReference 可以将字符实体报告为离散事件,然后应用程序开发人员可以选择解析或通过未解析的事件。默认情况下,实体已解析。或者,如果您不想将实体报告为事件,则可替换替换文本并将其报告为字符
ProcessingInstruction 报告基础处理指令的目标和数据。
评论 返回注释的文本。
EndDocument 报告一组 XML 事件的结束。
DTD 报告java.lang.String有关与流关联的 DTD 信息(如果有),并提供返回 DTD 中找到的自定义对象的方法。
属性 属性通常作为StartElement事件的一部分报告。但是,有时需要将属性作为独立的属性事件返回;例如,当XQueryXPath表达式返回命名空间时。
命名空间 与属性一样,命名空间通常作为StartElement的一部分进行报告,但有时需要将命名空间报告为离散命名空间事件。

请注意, DTDEntityDeclarationEntityReferenceNotationDeclarationProcessingInstruction事件仅在文档中创建正在处理包含 DTD。

事件映射示例

作为事件迭代器 API 如何映射 XML 流的示例,请考虑以下 XML 文档:

  1. <?xml version="1.0"?>
  2. <BookCatalogue xmlns="http://www.publishing.org">
  3. <Book>
  4. <Title>Yogasana Vijnana: the Science of Yoga</Title>
  5. <ISBN>81-40-34319-4</ISBN>
  6. <Cost currency="INR">11.50</Cost>
  7. </Book>
  8. </BookCatalogue>

该文档将被分析为十八个主要和次要事件,如下表所示。请注意,大括号( {} )中显示的辅助事件通常是从主事件而不是直接访问的。

Example of Iterator API Event Mapping

元素/属性 事件
1 version="1.0" StartDocument
2 isCData = false data = "\n" IsWhiteSpace = true Character
3 qname = BookCatalogue:http://www.publishing.org attributes = null namespaces = {BookCatalogue" -&gt; http://www.publishing.org"} StartElement
4 qname = Book attributes = null namespaces = null StartElement
qname = Title attributes = null namespaces = null StartElement
6 isCData = false data = "Yogasana Vijnana: the Science of Yoga\n\t" IsWhiteSpace = false Character
7 qname = Title namespaces = null EndElement
8 qname = ISBN attributes = null namespaces = null StartElement
9 isCData = false data = "81-40-34319-4\n\t" IsWhiteSpace = false Character
10 qname = ISBN namespaces = null EndElement
11 qname = Cost attributes = {"currency" -&gt; INR} namespaces = null StartElement
12 isCData = false data = "11.50\n\t" IsWhiteSpace = false Character
13 qname = Cost namespaces = null EndElement
14 isCData = false data = "\n" IsWhiteSpace = true Character
15 qname = Book namespaces = null EndElement
16 isCData = false data = "\n" IsWhiteSpace = true Character
17 qname = BookCatalogue:http://www.publishing.org namespaces = {BookCatalogue" -&gt; http://www.publishing.org"} EndElement
18 EndDocument

在这个例子中有几点需要注意:

  • 事件按照文档中遇到相应 XML 元素的顺序创建,包括元素嵌套,元素的打开和关闭,属性顺序,文档开始和文档结束等。

  • 与适当的 XML 语法一样,所有容器元素都有相应的开始和结束事件;例如,每个StartElement都有一个相应的EndElement ,即使对于空元素也是如此。

  • 属性事件被视为次要事件,并从相应的StartElement事件中访问。

  • 属性事件类似,名称空间事件被视为次要事件,但出现两次并且在事件流中可访问两次,首先来自其对应的StartElement ,然后从他们对应的EndElement

  • 为所有元素指定了字符事件,即使这些元素没有字符数据。类似地, Character事件可以跨事件分割。

  • StAX 解析器维护一个名称空间堆栈,该堆栈包含有关为当前元素及其祖先定义的所有 XML 名称空间的信息。通过javax.xml.namespace.NamespaceContext接口公开的命名空间堆栈可以通过命名空间前缀或 URI 访问。

在 Cursor 和 Iterator API 之间进行选择

在这一点上提出“我应该选择什么 API?我应该创建XMLStreamReaderXMLEventReader的实例吗?为什么还有两种 API 呢?“

发展目标

StAX 规范的作者针对三种类型的开发人员:

  • 图书馆和基础设施开发人员:创建应用服务器,JAXM,JAXB,JAX-RPC 和类似的实现;需要高效,低级的 API,并且可扩展性要求最低。

  • Java ME 开发人员:需要小型,简单的拉解析库,并且具有最小的可扩展性需求。

  • Java 平台,企业版(Java EE)和 Java 平台,标准版(Java SE)开发人员:需要干净,高效的拉解析库,需要灵活地读取和写入 XML 流,创建新的事件类型,并扩展 XML 文档元素和属性。

鉴于这些广泛的开发类别,StAX 作者认为定义两个小而有效的 API 更有用,而不是重载一个更大且必然更复杂的 API。

比较 Cursor 和 Iterator API

在光标和迭代器 API 之间进行选择之前,您应该注意一些可以使用游标 API 无法执行的迭代器 API:

  • XMLEvent子类创建的对象是不可变的,可以在数组,列表和映射中使用,甚至在解析器转移到后续事件之后也可以通过您的应用程序传递。

  • 您可以创建XMLEvent的子类型,它们是完全新的信息项或现有项的扩展,但使用其他方法。

  • 您可以使用比游标 API 更简单的方式在 XML 事件流中添加和删除事件。

同样,在做出选择时,请记住一些一般性建议:

  • 如果您正在为特定于内存的环境(如 Java ME)编程,则可以使用游标 API 生成更小,更高效的代码。

  • 如果性能是您的最高优先级 - 例如,在创建低级库或基础结构时 - 游标 API 更有效。

  • 如果要创建 XML 处理管道,请使用迭代器 API。

  • 如果要修改事件流,请使用迭代器 API。

  • 如果希望应用程序能够处理事件流的可插入处理,请使用迭代器 API。

  • 通常,如果您没有强烈的偏好,建议使用迭代器 API,因为它更灵活和可扩展,从而“面向未来”您的应用程序。