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是XMLStreamReader
和XMLStreamWriter
。 XMLStreamReader
包括用于从 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
上调用方法,例如getText
和getName
,以在当前光标位置获取数据。 XMLStreamWriter
提供与StartElement
和EndElement
事件类型相对应的方法;例如:
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是XMLEventWriter
。 XMLEventReader
interface包含五个方法,其中最重要的是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 Type | Description |
---|---|
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 事件作为属性;例如,当XQuery 或XPath 表达式的结果返回名称空间时。 |
Namespace | 与属性一样,名称空间通常报告为StartElement 的一部分,但是有时需要将名称空间报告为离散的Namespace 事件。 |
请注意,仅当要处理的文档包含 DTD 时,才会创建DTD
,EntityDeclaration
,EntityReference
,NotationDeclaration
和ProcessingInstruction
事件。
事件 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/Attribute | Event |
---|---|---|
1 | version="1.0" | StartDocument |
2 | isCData = false data = "\n" IsWhiteSpace = true | Characters |
3 | qname = BookCatalogue:http://www.publishing.org attributes = null namespaces = {BookCatalogue" -> http://www.publishing.org"} | StartElement |
4 | qname = Book attributes = null namespaces = null | StartElement |
5 | qname = Title attributes = null namespaces = null | StartElement |
6 | isCData = false data = "Yogasana Vijnana: the Science of Yoga\n\t" IsWhiteSpace = false | Characters |
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 | Characters |
10 | qname = ISBN namespaces = null | EndElement |
11 | qname = Cost attributes = {"currency" -> INR} namespaces = null | StartElement |
12 | isCData = false data = "11.50\n\t" IsWhiteSpace = false | Characters |
13 | qname = Cost namespaces = null | EndElement |
14 | isCData = false data = "\n" IsWhiteSpace = true | Characters |
15 | qname = Book namespaces = null | EndElement |
16 | isCData = false data = "\n" IsWhiteSpace = true | Characters |
17 | qname = BookCatalogue:http://www.publishing.org namespaces = {BookCatalogue" -> http://www.publishing.org"} | EndElement |
18 | EndDocument |
此示例中有几件重要的事情要注意:
-
按照在文档中遇到相应 XML 元素的 Sequences 创建事件,包括元素的嵌套,元素的打开和关闭,属性 Sequences,文档开始和文档结束等等。
-
与正确的 XML 语法一样,所有容器元素都具有相应的开始和结束事件。例如,即使是空元素,每个
StartElement
都有一个对应的EndElement
。 -
Attribute
事件被视为次要事件,并从其相应的StartElement
事件进行访问。 -
与
Attribute
事件类似,Namespace
事件被视为次要事件,但在事件流中出现两次并且可以两次访问,首先是从它们对应的StartElement
然后从它们对应的EndElement
进行。 -
为所有元素指定
Character
事件,即使这些元素没有字符数据也是如此。同样,可以将Character
个事件拆分为多个事件。 -
StAX 解析器维护一个名称空间堆栈,其中包含有关为当前元素及其祖先定义的所有 XML 名称空间的信息。通过
javax\.xml\.namespace\.NamespaceContext
interface公开的名称空间堆栈可以通过名称空间前缀或 URI 访问。
在游标和迭代器 API 之间进行选择
在这一点上合理地问:“我应该选择什么 API?我应该创建XMLStreamReader
或XMLEventReader
的实例吗?为什么仍然有两种 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”。