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 文档。这个光标可以一次指向一个东西,并且始终向前移动,从不向后移动,通常一次移动一个信息集元素。
两个主要的游标接口是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 信息作为字符串返回,从而最大限度地减少了对象分配要求。
迭代器 API
StAX 迭代器 API 将 XML 文档流表示为一组离散事件对象。这些事件由应用程序提取,并由解析器按照在源 XML 文档中读取它们的顺序提供。
基本迭代器接口称为XMLEvent
, XMLEvent 表中列出的每种事件类型都有子接口。用于读取迭代器事件的主要解析器接口是XMLEventReader
,用于编写迭代器事件的主要接口是XMLEventWriter
。 XMLEventReader
接口包含五种方法,其中最重要的是nextEvent
,它返回 XML 流中的下一个事件。 XMLEventReader
实现了java.util.Iterator
,这意味着来自XMLEventReader
的返回可以被缓存或传递到可以与标准 Java Iterator 一起使用的例程中;例如:
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
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 事件的一部分报告。但是,有时需要将属性作为独立的属性 事件返回;例如,当XQuery 或XPath 表达式返回命名空间时。 |
命名空间 |
与属性一样,命名空间通常作为StartElement 的一部分进行报告,但有时需要将命名空间报告为离散命名空间 事件。 |
请注意, DTD
, EntityDeclaration
, EntityReference
, NotationDeclaration
和ProcessingInstruction
事件仅在文档中创建正在处理包含 DTD。
事件映射示例
作为事件迭代器 API 如何映射 XML 流的示例,请考虑以下 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>
该文档将被分析为十八个主要和次要事件,如下表所示。请注意,大括号( {}
)中显示的辅助事件通常是从主事件而不是直接访问的。
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" -> 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" -> 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" -> 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?我应该创建XMLStreamReader
或XMLEventReader
的实例吗?为什么还有两种 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,因为它更灵活和可扩展,从而“面向未来”您的应用程序。