Java 中文官方教程 2022 版(四十)(1)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Java 中文官方教程 2022 版(四十)

课程:XML 的流式 API

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

本课程专注于 XML 的流式 API(StAX),这是一种基于 Java 技术的流式、事件驱动、拉取解析的 API,用于读取和写入 XML 文档。StAX 使您能够创建快速、相对易于编程且具有轻量级内存占用的双向 XML 解析器。

为什么选择 StAX?

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

StAX 项目由 BEA 主导,得到了 Sun Microsystems 的支持,JSR 173 规范于 2004 年 3 月通过了 Java 社区流程的最终批准投票。StAX API 的主要目标是通过公开一个简单的基于迭代器的 API,将“解析控制权交给程序员。这允许程序员请求下一个事件(拉取事件),并允许以过程化方式存储状态。” StAX 的创建是为了解决两种最常见解析 API,SAX 和 DOM,的限制。

流式处理与 DOM

一般来说,处理 XML 信息集有两种编程模型:流式处理文档对象模型(DOM)。

DOM 模型涉及创建代表整个文档树和 XML 文档的完整信息集状态的内存对象。一旦在内存中,DOM 树可以自由导航和任意解析,因此为开发人员提供了最大的灵活性。然而,这种灵活性的代价是潜在的大内存占用和显著的处理器需求,因为整个文档的表示必须作为对象在内存中保持,以便在文档处理期间使用。在处理小型文档时,这可能不是问题,但随着文档大小的增加,内存和处理器需求可能会迅速升高。

流式处理是指一种编程模型,在应用程序运行时串行传输和解析 XML 信息集,通常是实时的,并且通常来自动态来源,其内容事先并不完全知晓。此外,基于流的解析器可以立即开始生成输出,并且信息集元素在使用后可以立即丢弃和进行垃圾回收。虽然提供了较小的内存占用、降低的处理器需求和在某些情况下更高的性能,但流处理的主要折衷是您只能在文档中的一个位置看到信息集状态。您基本上受限于文档的“纸板筒”视图,这意味着您需要在阅读 XML 文档之前知道要进行哪些处理。

在处理 XML 时,流式处理模型特别适用于应用程序具有严格的内存限制,比如在运行 Java 平台微版(Java ME 平台)的手机上,或者当应用程序需要同时处理多个请求时,比如在应用服务器上。实际上,可以说大多数 XML 业务逻辑都可以从流式处理中受益,并且不需要在内存中维护整个 DOM 树。

拉取解析与推送解析

拉取解析是一种编程模型,其中客户端应用程序在需要与 XML 信息集交互时调用 XML 解析库的方法,即客户端只有在明确请求时才会获取(拉取)XML 数据。

推送解析是一种编程模型,其中 XML 解析器在遇到 XML 信息集中的元素时向客户端发送(推送)XML 数据,即使客户端此时还没有准备好使用它。

在处理 XML 流时,拉取解析相比于推送解析提供了几个优势:

  • 在拉取解析中,客户端控制应用程序线程,并且可以在需要时调用解析器的方法。相比之下,在推送处理中,解析器控制应用程序线程,客户端只能接受解析器的调用。
  • 拉取解析库可以比推送库更小,与这些库交互的客户端代码也更简单,即使对于更复杂的文档。
  • 拉取客户端可以使用单个线程同时读取多个文档。
  • StAX 拉取解析器可以过滤 XML 文档,使客户端不需要的元素被忽略,并且可以支持非 XML 数据的 XML 视图。

StAX 使用案例

StAX 规范定义了 API 的许多用例:

  • 数据绑定
  • 反编组 XML 文档
  • 将 XML 文档编组
  • 并行文档处理
  • 无线通信
  • 简单对象访问协议(SOAP)消息处理
  • 解析简单可预测的结构
  • 解析具有前向引用的图形表示
  • 解析 Web 服务描述语言(WSDL)
  • 虚拟数据源
  • 查看存储在数据库中的 XML 数据
  • 查看由 XML 数据绑定创建的 Java 对象中的数据
  • 将 DOM 树作为事件流导航
  • 解析特定的 XML 词汇
  • 管道化 XML 处理

对所有这些用例的完整讨论超出了本课程的范围。请参考 StAX 规范以获取更多信息。

将 StAX 与其他 JAXP API 进行比较

作为 JAXP 家族中的一个 API,StAX 可以与 SAX、TrAX 和 JDOM 等其他 API 进行比较。在后两者中,StAX 不像 TrAX 或 JDOM 那样强大或灵活,但也不需要太多内存或处理器负载才能发挥作用,并且在许多情况下,StAX 可以胜过基于 DOM 的 API。上面概述的相同论点,权衡 DOM 模型与流模型的成本/效益,在这里同样适用。

有鉴于此,最接近的比较可以在 StAX 和 SAX 之间进行,正是在这里 StAX 提供了许多情况下有益的功能;其中一些包括:

  • 使用 StAX 的客户端通常比使用 SAX 的客户端更容易编码。虽然可以说 SAX 解析器稍微更容易编写,但 StAX 解析器的代码可能更小,客户端与解析器交互所需的代码更简单。
  • StAX 是一个双向 API,意味着它既可以读取又可以写入 XML 文档。SAX 只能读取,所以如果你想要写入 XML 文档,就需要另一个 API。
  • SAX 是一个推送 API,而 StAX 是一个拉取 API。上面概述的推送和拉取 API 之间的权衡在这里也适用。

以下表格总结了 StAX、SAX、DOM 和 TrAX 的比较特性。(表格改编自 Jeff Ryan 的文章Does StAX Belong in Your XML Toolbox?)。

XML 解析器 API 特性摘要

特性 StAX SAX DOM TrAX
API 类型 拉取,流式 推送,流式 内存树 XSLT 规则
使用便捷性
XPath 能力
CPU 和内存效率 良好 良好 各异 各异
仅向前
读取 XML
写入 XML
创建,读取,更新,删除

StAX API

原文: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 信息模型中检索所有可能信息的访问方法,包括文档编码、元素名称、属性、命名空间、文本节点、起始标记、注释、处理指令、文档边界等等;例如:

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 信息作为字符串返回,这减少了对象分配的需求。

迭代器 API

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

基本的迭代器接口称为XMLEvent,并且为事件迭代器 API 表中列出的每种事件类型都有子接口。用于读取迭代器事件的主要解析器接口是XMLEventReader,用于写入迭代器事件的主要接口是XMLEventWriterXMLEventReader接口包含五种方法,其中最重要的是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 中定义的类型

事件类型 描述
StartDocument 报告一组 XML 事件的开始,包括编码、XML 版本和独立属性。
StartElement 报告元素的开始,包括任何属性和命名空间声明;还提供了开始标记的前缀、命名空间 URI 和本地名称的访问。
EndElement 报告元素的结束标记。如果已在相应的 StartElement 上显式设置了命名空间,则在此处可以调用已经超出范围的命名空间。
Characters 对应于 XML CData 部分和 CharacterData 实体。请注意,可忽略的空格和重要的空格也被报告为 Character 事件。
EntityReference 字符实体可以作为独立事件报告,应用程序开发人员可以选择解析或传递未解析的实体。默认情况下,实体会被解析。或者,如果不想将实体报告为事件,则可以替换文本并报告为 Characters
ProcessingInstruction 报告底层处理指令的目标和数据。
Comment 返回注释的文本。
EndDocument 报告一组 XML 事件的结束。
DTD 报告与流相关联的(如果有的话)DTD 的信息,并提供一种返回在 DTD 中找到的自定义对象的方法。
Attribute 属性通常作为 StartElement 事件的一部分报告。然而,有时希望将属性作为独立的 Attribute 事件返回;例如,当命名空间作为 XQueryXPath 表达式的结果返回时。
Namespace 与属性一样,命名空间通常作为 StartElement 的一部分报告,但有时希望将命名空间作为独立的 Namespace 事件报告。

请注意,只有在处理的文档包含 DTD 时,才会创建 DTDEntityDeclarationEntityReferenceNotationDeclarationProcessingInstruction 事件。

事件映射示例

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

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

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

迭代器 API 事件映射示例

# 元素/属性 事件
1 version="1.0" StartDocument
2 isCData = false data = "\n" IsWhiteSpace = true Characters
3 qname = BookCatalogue:http://www.publishing.org 属性 = null 命名空间 = {BookCatalogue" -> http://www.publishing.org"} StartElement
4 qname = 书 属性 = null 命名空间 = null StartElement
5 qname = 标题 属性 = null 命名空间 = 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 元素的顺序创建,包括元素的嵌套、打开和关闭元素、属性顺序、文档开始和文档结束等。
  • 与正确的 XML 语法一样,所有容器元素都有相应的开始和结束事件;例如,每个 StartElement 都有一个对应的 EndElement,即使是空元素也是如此。
  • Attribute 事件被视为次要事件,并且可以从其对应的 StartElement 事件中访问。
  • Attribute 事件类似,Namespace 事件被视为次要事件,但在事件流中出现两次,并且可以从它们对应的 StartElementEndElement 中分别访问两次。
  • 所有元素都指定了 Character 事件,即使这些元素没有字符数据。同样,Character 事件可以跨事件分割。
  • StAX 解析器维护一个命名空间堆栈,其中保存了当前元素及其祖先元素定义的所有 XML 命名空间信息。通过 javax.xml.namespace.NamespaceContext 接口暴露的命名空间堆栈可以通过命名空间前缀或 URI 访问。

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

此时合理地问一下,“我应该选择哪个 API?我应该创建 XMLStreamReader 还是 XMLEventReader 的实例?为什么会有两种类型的 API?”

开发目标

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

  • 图书馆和基础设施开发者:创建应用服务器、JAXM、JAXB、JAX-RPC 等实现;需要高效、低级别的 API,并且具有最小的可扩展性要求。
  • Java ME 开发者:需要小型、简单的拉取解析库,并且具有最小的可扩展性需求。
  • Java 平台企业版(Java EE)和 Java 平台标准版(Java SE)开发人员:需要干净、高效的拉取解析库,同时需要灵活性来读取和写入 XML 流,创建新的事件类型,并扩展 XML 文档元素和属性。

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

比较游标和迭代器 API

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

  • XMLEvent子类创建的对象是不可变的,可以在数组、列表和映射中使用,并且可以在解析器继续处理后传递到你的应用程序中。
  • 你可以创建XMLEvent的子类型,这些子类型可以是全新的信息项,也可以是现有项目的扩展,但具有额外的方法。
  • 你可以以比游标 API 更简单的方式向 XML 事件流中添加和删除事件。

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

  • 如果你正在为特别受内存限制的环境编程,比如 Java ME,你可以使用游标 API 创建更小、更高效的代码。
  • 如果性能是你的最高优先级——例如,在创建低级库或基础设施时——游标 API 更有效率。
  • 如果你想创建 XML 处理管道,请使用迭代器 API。
  • 如果你想修改事件流,请使用迭代器 API。
  • 如果你希望你的应用程序能够处理事件流的可插拔处理,请使用迭代器 API。
  • 一般来说,如果你没有明确偏好,建议使用迭代器 API,因为它更灵活、可扩展,从而“未雨绸缪”你的应用程序。

使用 StAX

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

一般来说,StAX 程序员通过使用 XMLInputFactoryXMLOutputFactoryXMLEventFactory 类来创建 XML 流读取器、写入器和事件。通过在工厂上设置属性来进行配置,可以通过在工厂上使用 setProperty 方法将特定于实现的设置传递给底层实现。类似地,可以使用 getProperty 工厂方法查询特定于实现的设置。

下面描述了 XMLInputFactoryXMLOutputFactoryXMLEventFactory 类,然后讨论了资源分配、命名空间和属性管理、错误处理,最后使用游标和迭代器 API 读取和写入流。

StAX 工厂类

StAX 工厂类。XMLInputFactoryXMLOutputFactoryXMLEventFactory,让您定义和配置 XML 流读取器、流写入器和事件类的实现实例。

XMLInputFactory

XMLInputFactory 类允许您配置由工厂创建的 XML 流读取器处理器的实现实例。通过在类上调用 newInstance 方法来创建抽象类 XMLInputFactory 的新实例。然后使用静态方法 XMLInputFactory.newInstance 来创建新的工厂实例。

派生自 JAXP,XMLInputFactory.newInstance 方法通过以下查找过程确定要加载的特定 XMLInputFactory 实现类:

  1. 使用 javax.xml.stream.XMLInputFactory 系统属性。
  2. 使用 Java SE 平台的 Java Runtime Environment (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 属性

属性 描述
isValidating 打开实现特定的验证。
isCoalescing (必需) 要求处理器合并相邻的字符数据。
isNamespaceAware 关闭命名空间支持。所有实现必须支持命名空间。对非命名空间感知文档的支持是可选的。
isReplacingEntityReferences (必需) 要求处理器用其替换值替换内部实体引用,并将其报告为字符或描述实体的事件集。
isSupportingExternalEntities (必需) 要求处理器解析外部解析实体。
reporter (必需) 设置并获取XMLReporter接口的实现。
resolver (必需) 设置并获取XMLResolver接口的实现。
allocator (必需) 设置并获取XMLEventAllocator接口的实现。

XMLOutputFactory

通过在类上调用newInstance方法来创建抽象类XMLOutputFactory的新实例。然后使用静态方法XMLOutputFactory.newInstance来创建一个新的工厂实例。用于获取实例的算法与XMLInputFactory相同,但引用javax.xml.stream.XMLOutputFactory系统属性。

XMLOutputFactory 只支持一个属性,即javax.xml.stream.isRepairingNamespaces。此属性是必需的,其目的是创建默认前缀并将其与命名空间 URI 关联起来。有关更多信息,请参阅 StAX 规范。

XMLEventFactory

通过在类上调用newInstance方法来创建抽象类XMLEventFactory的新实例。然后使用静态方法XMLEventFactory.newInstance来创建一个新的工厂实例。此工厂引用javax.xml.stream.XMLEventFactory属性来实例化工厂。用于获取实例的算法与XMLInputFactoryXMLOutputFactory相同,但引用javax.xml.stream.XMLEventFactory系统属性。

XMLEventFactory 没有默认属性。

Java 中文官方教程 2022 版(四十)(2)https://developer.aliyun.com/article/1488166

相关文章
|
11天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
2月前
|
Java 开发者 UED
【实战宝典】Java异常处理大师级教程:throws关键字,让异常声明成为你的专属标签!
【实战宝典】Java异常处理大师级教程:throws关键字,让异常声明成为你的专属标签!
48 3
|
6天前
|
Java 数据库连接 编译器
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
Kotlin教程笔记(29) -Kotlin 兼容 Java 遇到的最大的“坑”
|
8天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
本系列教程笔记详细讲解了Kotlin语法,适合希望深入了解Kotlin的开发者。对于需要快速学习Kotlin的小伙伴,推荐查看“简洁”系列教程。本篇笔记重点介绍了Kotlin与Java混编的技巧,包括代码转换、类调用、ProGuard问题、Android库开发建议以及相互调用时的注意事项。
14 3
|
10天前
|
Java 编译器 Android开发
Kotlin教程笔记(28) -Kotlin 与 Java 混编
Kotlin教程笔记(28) -Kotlin 与 Java 混编
18 3
|
11天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
17天前
|
消息中间件 存储 JSON
rabbitmq基础教程(ui,java,springamqp)
本文提供了RabbitMQ的基础教程,包括如何使用UI创建队列和交换机、Java代码操作RabbitMQ、Spring AMQP进行消息发送和接收,以及如何使用不同的交换机类型(fanout、direct、topic)进行消息路由。
13 0
rabbitmq基础教程(ui,java,springamqp)
|
18天前
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
117 1
|
9天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(2)
|
9天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(1)