StAX API

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

StAX API 实际上是两个截然不同的 API 集:“ cursor” API 和“ iterator” API。本类稍后将详细解释这两个 API 集,但下面将简要介绍它们的主要功能。

Cursor API

顾名思义,StAX cursor API 表示一个游标,您可以使用它从头到尾遍历 XML 文档。该光标一次可以指向一件事,并且总是向前移动,从不向后移动,通常一次指向一个信息集元素。

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

public interface XMLStreamReader {
    public int next() throws XMLStreamException;
    public boolean hasNext() throws XMLStreamException;

    public String getText();
    public String getLocalName();
    public String getNamespaceURI();
    // ... other methods not shown
}

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

public interface XMLStreamWriter {
    public void writeStartElement(String localName) throws XMLStreamException;
    public void writeEndElement() throws XMLStreamException;
    public void writeCharacters(String text) throws XMLStreamException;
    // ... other methods not shown
}

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

Iterator API

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

基本迭代器interface称为XMLEvent,并且XMLEvent table中列出了每种事件类型的子interface。用于读取迭代器事件的主要分析器interface是XMLEventReader,而用于写入迭代器事件的主要interface是XMLEventWriterXMLEventReaderinterface包含五个方法,其中最重要的是nextEvent,它返回 XML 流中的下一个事件。 XMLEventReader实现java\.util\.Iterator,这意味着XMLEventReader的返回值可以被缓存或传递到可以与标准 Java 迭代器一起使用的例程中。例如:

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

同样,在迭代器 API 的输出端,您具有:

public interface XMLEventWriter {
    public void flush() throws XMLStreamException;
    public void close() throws XMLStreamException;
    public void add(XMLEvent e) throws XMLStreamException;
    public void add(Attribute attribute) throws XMLStreamException;
    // ...
}

迭代器事件类型

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

请注意,仅当要处理的文档包含 DTD 时,才会创建DTDEntityDeclarationEntityReferenceNotationDeclarationProcessingInstruction事件。

事件 Map 示例

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

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

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

  • Iterator API 事件 Map 的示例*
#Element/AttributeEvent
1version="1.0"StartDocument
2isCData = false data = "\n" IsWhiteSpace = trueCharacters
3qname = BookCatalogue:http://www.publishing.org attributes = null namespaces = {BookCatalogue" -> http://www.publishing.org"}StartElement
4qname = Book attributes = null namespaces = nullStartElement
5qname = Title attributes = null namespaces = nullStartElement
6isCData = false data = "Yogasana Vijnana: the Science of Yoga\n\t" IsWhiteSpace = falseCharacters
7qname = Title namespaces = nullEndElement
8qname = ISBN attributes = null namespaces = nullStartElement
9isCData = false data = "81-40-34319-4\n\t" IsWhiteSpace = falseCharacters
10qname = ISBN namespaces = nullEndElement
11qname = Cost attributes = {"currency" -> INR} namespaces = nullStartElement
12isCData = false data = "11.50\n\t" IsWhiteSpace = falseCharacters
13qname = Cost namespaces = nullEndElement
14isCData = false data = "\n" IsWhiteSpace = trueCharacters
15qname = Book namespaces = nullEndElement
16isCData = false data = "\n" IsWhiteSpace = trueCharacters
17qname = BookCatalogue:http://www.publishing.org namespaces = {BookCatalogue" -> http://www.publishing.org"}EndElement
18EndDocument

此示例中有几件重要的事情要注意:

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

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

  • Attribute事件被视为次要事件,并从其相应的StartElement事件进行访问。

  • Attribute事件类似,Namespace事件被视为次要事件,但在事件流中出现两次并且可以两次访问,首先是从它们对应的StartElement然后从它们对应的EndElement进行。

  • 为所有元素指定Character事件,即使这些元素没有字符数据也是如此。同样,可以将Character个事件拆分为多个事件。

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

在游标和迭代器 API 之间进行选择

在这一点上合理地问:“我应该选择什么 API?我应该创建XMLStreamReaderXMLEventReader的实例吗?为什么仍然有两种 API?”

Development Goals

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

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

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

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

考虑到这些广泛的开发类别,StAX 作者认为定义两个小型高效的 API 比重载一个更大且必然更复杂的 API 更为有用。

比较游标和迭代器 API

在游标和迭代器 API 之间进行选择之前,应该注意一些可以使用迭代器 API 进行,而不能使用游标 API 进行的事情:

  • XMLEvent子类创建的对象是不可变的,可以在数组,列表和 Map 中使用,并且即使在解析器移至后续事件之后也可以在应用程序中传递。

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

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

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

  • 如果要针对特别受内存限制的环境(例如 Java ME)进行编程,则可以使用游标 API 编写更小巧,更有效的代码。

  • 如果将性能放在首位(例如,在创建低级库或基础结构时),则游标 API 会更高效。

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

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

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

  • 通常,如果您没有一种或另一种强烈的偏好,建议您使用迭代器 API,因为它更加灵活和可扩展,因此可以“使应用程序面向 Future”。