XML 解析器通常是高性能、健壮应用程序的关键。传统的 XML 解析技术包括文档对象模型(Document Object Model,DOM)和 Simple API for XML (SAX)。现在有一种非常不错的名为 Streaming API for XML (StAX) 的创新型新解析技术,它是与 Java™ Platform, Enterprise Edition (Java EE) 5 规范集成的。Apache Geronimo 2.0 是 Java EE 5 的完整实现,它包括 StAX 解析器 —— Codehaus 的 Woodstox。在此部分中,您将了解 StAX 的优点以及 Geronimo 团队选择 Woodstox 作为 StAX 解析器的原因。

XML 的重要性

XML 是由 Tim Bray 和 Michael Sperberg-McQueen 于 1996 年引入的。它的潜力已获得广泛公认,但是很难想象那时候会有哪个人能够知道 XML 会成为怎样的一种主要技术。企业 Java 开发人员将使用 XML 用于配置、用作数据存储以及最常见的是用作数据交换的格式。它是 Web 服务和 SOAP 的基础,从而也是现代面向服务的架构(Service-Oriented Architecture,SOA)设计模式的基础。但是 XML 并没有止步在那里。它将 X 融入 Ajax 或 Asynchronous JavaScript + XML 中,成为现代 Web 应用程序能够提供前所未有的丰富体验的关键。

但是,XML 也不是包治百病的灵丹妙药;它也有不足之处。XML 文档往往都很大。XML 文档都有通用树结构,但是这些 XML 文档的可扩展性意味着它的模式可以千变万化。这些方面向高效解析 XML 提出了挑战。克服 XML 解析挑战的传统方法有两种:DOM 和 SAX。

XML 处理:DOM 和 SAX

DOM 和 SAX 是解析 XML 的两种典型策略。它们在许多方面都是性质对立的策略。DOM 将为 XML 文档提供一个简单的对象模型。DOM 解析器将把 XML 文档转换成表示文档中所有数据的易于使用的对象。但是,这样如实地表示 XML 文档需要付出一定的代价:DOM 解析往往需要占用很多内存。

内存对 SAX 来说不是问题。SAX 解析器将生成一系列解析事件。注册这些事件的回调并随后对来自这些事件的数据执行某种逻辑都由 handler 来完成。它快速且高效,但是要求有复杂的编程模型。

了解使用 DOM 与 SAX 之间差异的最简单方法 —— 并且因而了解 StAX 的动机和优点 —— 是查看具体示例。

 

回页首

使用 Flickr 解析示例

找到一些 XML 来解析并不难。到处都在使用 XML。现在的大多数 Web 站点都提供了某种基于 XML 的 Web 服务。Flickr 是归 Yahoo 所有的一个流行的照片分享站点,它拥有强大而灵活的 API。让我们来看一看访问 Flickr 的 “有趣” 照片的一些简单代码(要获得本文中使用的所有源代码,请参阅 下载,并确保把 Woodstox 放入类路径中或者使用 JDK 1.6)。代码如清单 1 所示:

清单 1. 使用 Flickr API
String apiKey = "c4579586f41a90372f762cb65c78be5d";
String urlStr = "http://api.flickr.com/services/rest/?" +
"method=flickr.interestingness.getList&per_page=20&api_key="+apiKey;
URL request = new URL(urlStr);
InputStream input = request.openStream();

这段代码将使用 Flickr 的代表性状态传输(Representational State Transfer,REST)API(有关 Flickr 的 API 和 REST 格式的更多信息,请参阅 参考资料 部分)。以上调用的一些样例输出如清单 2 所示:

清单 2. Flickr 的 XML
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="25" per_page="20" total="500">
<photo id="469774979" owner="35373726@N00" secret="c8a1be2012" server="183"
farm="1" title="Where will it lead me......?" ispublic="1" isfriend="0"
isfamily="0" />
<photo id="470281793" owner="73955226@N00" secret="49612a2794" server="212"
farm="1" title="Island Beauty" ispublic="1" isfriend="0" isfamily="0" />
<photo id="469808244" owner="43568064@N00" secret="26b71544a3" server="227"
farm="1" title="" ispublic="1" isfriend="0" isfamily="0" />
</photos>
</rsp>

注意清单 2 只显示了三张照片。API 调用实际上将返回 20(URL 字符串中的 per_page 参数)。结果十分简单,因此来看一看如何解析这个 XML。在示例中,将解析出每张照片的标题及其 ID。该 ID 可用于创建该照片的 URL,因此不难想象 Web 应用程序(可能是 mashup)只使用此信息。首先需要使用 DOM 来提取此数据。

 

回页首

DOM 示例

要使用 DOM,需要把文档解析成文档对象。这是表示已被解析的 XML 文档的内存中树结构。随后遍历 DOM 树来查找每张照片的标题和 ID。将此数据放入简单映射中。完成此过程的代码如清单 3 所示:

清单 3. 用 DOM 进行解析
Map<String,String> map = new HashMap<String,String>();
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse(input);
Element root = dom.getDocumentElement();
NodeList childNodes = root.getChildNodes();
Node photosNode = null;
for (int i=0;i<childNodes.getLength();i++){
Node node = childNodes.item(i);
if (node.getNodeName().equalsIgnoreCase("photos")){
photosNode = node;
break;
}
}
childNodes = photosNode.getChildNodes();
for (int i=0;i<childNodes.getLength();i++){
Node node = childNodes.item(i);
if (node.getNodeName().equalsIgnoreCase("photo")){
String title = node.getAttributes().getNamedItem("title").getTextContent();
String id = node.getAttributes().getNamedItem("id").getTextContent();
map.put(id,title);
}
}

DOM 十分流行,因为它非常易于使用。只需将输入源传入解析器中,然后解析器将为您提供 document 对象。然后,您可以遍历子节点,直至找到照片节点。每个照片节点都是照片节点的子节点,因此您将遍历每个照片节点,然后访问每个照片节点的 title 和 id 属性并将其存储到映射中。

但是,DOM 也有一些明显的效率低下之处。您要存储大量的可能并不关心的数据,例如每张照片的所有者。您还将浏览所有数据两次:第一次浏览用于将其读入文档对象,然后在遍历文档对象时进行第二次浏览。避免这些效率低下之处的传统方法是使用 SAX。

 

回页首

SAX 示例

SAX 解析器并不像 DOM 解析器一样返回一个精密的 document 对象。相反,SAX 解析器将在遍历 XML 文档时给出一系列事件。必须通过实现接口或者扩展 DefaultHandler 类并根据需要重写它的方法,创建这些事件的 handler。清单 4 将演示 Flickr XML 文档的 SAX 解析。

清单 4. 用 SAX 进行解析
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
DefaultHandler handler = new DefaultHandler(){
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("photo")){
String title = attributes.getValue("title");
String id = attributes.getValue("id");
// map is static so we can access it here
map.put(id, title);
}
}
};
parser.parse(input, handler);

很明显,清单 4 中所示的代码比 清单 3 中的 DOM 代码难于理解一些。您需要使用 ContentHandler 来处理 SAX 事件,因此创建了DefaultHandler 并重写了它的 startElement 回调方法。查看它是不是一个照片元素,并且如果是照片元素,则访问它的 title 和 id 属性。

代码十分简洁并且在运行时非常高效。它仅存储您所关心的数据,并且只遍历文档一次。它是更为复杂的代码,要求扩展类才能注册事件侦听程序。如果能够高效地解析 XML 而且可以使用更直观的编程模型那就太好了。StAX 于是应运而生。

 

回页首

StAX 备选方案

SAX 中的复杂度来自它实现的 Observer 设计模式。它是一个 push 模型,因为解析器将把事件 push 到随后作用于事件的 observer 中。StAX 模型类似于 SAX。它将来自 XML 文档的数据和事件流线化,使其可以像 SAX 一样快速且高效。最大的不同之处是它使用 pull 模型。这将允许应用程序代码从解析器中 pull 事件。

这可能听起来像是一个细微的差异,但是它将允许使用更简单的编程模型。查阅清单 5 以查看 StAX 的运作。

清单 5. 用 StAX 进行解析
Map<String,String> map = new HashMap<String,String>();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
QName qId = new QName("id");
QName qTitle = new QName("title");
QName qPhoto = new QName("photo");
XMLEventReader reader = inputFactory.createXMLEventReader(input);
while (reader.hasNext()){
XMLEvent event = reader.nextEvent();
if (event.isStartElement()){
StartElement element = event.asStartElement();
if (element.getName().equals(qPhoto)){
String id = element.getAttributeByName(qId).getValue();
String title = element.getAttributeByName(qTitle).getValue();
map.put(id,title);
}
}
}
reader.close();

首先,您不必扩展任何类。那是因为您无需为事件进行注册。使用 StAX,您可以控制事件流,因为将从解析器中 pull 这些事件流。您可以使用一种熟悉的类似迭代器的语法来搜索整个文档以查找所需的数据。您仍将只存储所需的数据,并且只需浏览 XML 文档一次。您将获得与使用 SAX 时一样的效率,但是代码将直观得多。

 

回页首

使用 Woodstox 作为 Geronimo 的 StAX 提供程序

现在您已经看到了 StAX 解析的优点。它被广泛公认为 XML 技术中的重大进步。因而,当它成为 Java EE 5 规范的一部分时并不令人惊讶(它甚至还包含在 Java Platform, Standard Edition [Java SE] 6 中)。由于它是 Java EE 5 的一部分,因此它必须由 Geronimo 2.0 来实现。

Geronimo 团队十分幸运,有若干个开源 StAX 实现可供选择。团队选取了 Woodstox 作为 Geronimo 所附带的 StAX 解析器。Woodstox 被视为执行效果最佳的 StAX 实现之一(要比较各种 StAX 解析器,请参阅 参考资料)。此外,Woodstox 是在 Lesser General Public License (LGPL) 和 Apache 2.0 许可下双重授权的。因此您可以不受任何限制地将 Woodstox 及其源代码集成到 Geronimo 中。

应用程序性能调优:发挥 Woodstox 的最大功效

性能很明显是 Woodstox 带给 Geronimo 的优点之一。就像使用其他高性能技术一样,了解如何使用 Woodstox 才能获得最佳性能非常重要。清单 5 中的代码将使用 XMLEventReader 接口,这是 StAX 规范包含的一个高级 API。用于获得高性能的较低级 API 是 XMLStreamReader 接口。清单 6 显示了使用此接口的 StAX 解析器。

清单 6. 用 XMLStreamReader 进行 StAX 解析
Map<String,String> map = new HashMap<String,String>();
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
QName qId = new QName("id");
QName qTitle = new QName("title");
QName qPhoto = new QName("photo");
XMLStreamReader reader = inputFactory.createXMLStreamReader(input);
while (reader.hasNext()){
int event = reader.next();
if (event == START_ELEMENT){ // statically included constant from XMLStreamConstants
if (reader.getName().equals(qPhoto)){
String id = reader.getAttributeValue(null, qId.getLocalPart());
String title = reader.getAttributeValue(null, qTitle.getLocalPart());
map.put(id,title);
}
}
}
reader.close();

清单 6 中的代码类似于 清单 5 中的代码;虽然它很明显有些低级,但是您将获得很大的性能提高。

结束语

您已经了解了使用 StAX 解析器解析 XML 文档的一些优点。StAX 在 SAX 与 DOM 之间提供了很好的折衷。您可以通过使用它作为 Geronimo 2.0 的一部分立即利用 StAX。您不但将开始使用 StAX 的直观 pull API,而且将获得在 Woodstox 中使用 StAX 的高性能实现的额外优势。

 

回页首

下载

描述 名字 大小
样例文章代码 renegade.woodstox.source.zip 4KB

参考资料

学习

获得产品和技术

http://www.ibm.com/developerworks/cn/opensource/os-ag-renegade15/

Geronimo 叛逆者: 使用集成软件包:Codehaus 的 Woodstox(转载)的更多相关文章

  1. Linux通过XAMPP集成软件包搭建LAMPP环境

    前面介绍过一篇“Linux手动搭建LAMP环境”,今天再来整理一篇“Linux通过XAMPP集成软件包搭建LAMPP环境”. 其实当初整理通过XAMPP集成软件包搭建LAMPP环境的原因是这样的: 自 ...

  2. OpenWrt(LEDE)2020.4.29更新 UPnP+NAS+多拨+网盘+DNS优化+帕斯沃 无缝集成+软件包

    交流群:QQ 1030484865 电报:  t_homelede   固件说明 基于Lede OpenWrt R2020.4.8版本(源码截止2020.4.29)Lienol Feed及若干自行维护 ...

  3. HomeLede 2020.5.27更新 UPnP+NAS+多拨+网盘+DNS优化+帕斯沃/Clash 无缝集成+软件包

    交流群:QQ 1030484865 电报 t.me/t_homelede   固件说明 基于Lede OpenWrt R2020.5.20版本(源码截止2020.5.27)及若干自行维护的软件包 结合 ...

  4. Gitea v1.17.0 正式发布 | 集成软件包管理器、容器镜像仓库

    我们自豪地宣布 Gitea v1.17.0 发布了.本次发布带来了诸多新特性和累积的更新,我们强烈建议用户在更新到最新版本之前仔细阅读发行注记. 在 1.17.0 版本的开发中我们一共合并了 645 ...

  5. 建站集成软件包 XAMPP搭建后台系统与微信小程序开发

    下载安装XAMPP软件,运行Apache和MySQL 查看项目文件放在哪个位置可以正常运行 然后访问localhost即可 下载weiphp官网的weiapp(专为微信小程序开发使用)放在htdocs ...

  6. 移动应用开发测试工具Bugtags集成和使用教程【转载】

    前段时间,有很多APP突然走红,最终却都是樱花一现.作为一个创业团队,突然爆红是非常难得的机会.然并卵,由于没有经过充分的测试,再加上用户的激增,APP闪退.服务器数据异常等问题就被暴露出来,用户的流 ...

  7. RN集成echarts4图表组件react-native-secharts(转载)

    一个webview封装的图表组件.基于百度echarts4,相比native-echarts有echarts自带对象支持,例如渐变色等,用法与官网相同用法. echarts version 4.2.0 ...

  8. 基于Woodstox的StAX 2 (Streaming API for XML)解析XML

    StAX (Streaming API for XML)面向流的拉式解析XML,速度快.占用资源少,非常合适处理大数据量的xml文件. 详细教程和说明可以参见以下几篇文章: 使用 StAX 解析 XM ...

  9. 企业搜索引擎开发之连接器connector(十八)

    创建并启动连接器实例之后,连接器就会基于Http协议向指定的数据接收服务器发送xmlfeed格式数据,我们可以通过配置http代理服务器抓取当前基于http协议格式的数据(或者也可以通过其他网络抓包工 ...

随机推荐

  1. shell编写redis启动脚本

    ​ 安装后redis,默认系统不会自启动,如果关机或重启redis不会自行启动,linux下/etc/init.d/目录下基本上存放所有系统的大多数的启动脚本,放在这个目录下的脚本可以实现自启动操作. ...

  2. 学习笔记之C++ Primer中文版(第五版)

    非常权威系统的语言书,正好学习下C++11内容. C++ Primer_百度百科 http://baike.baidu.com/link?url=YLvDJE9w3CjGp3eQwjuXYKUZs7v ...

  3. 利用spring的CommonsMultipartResolver上传文件

    1.CommonsMultipartResolver是spring里面提供的一个上传方式,效率我不知道,但是加入spring容器管理还是很不错的. 2.先看依赖包pom.xml <project ...

  4. HttpContext.Current并非无处不在

    阅读目录 开始 无处不在的HttpContext HttpContext.Current到底保存在哪里? HttpContext并非无处不在! 如何获取文件绝对路径? 异步调用中如何访问HttpCon ...

  5. python博客

    https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

  6. jenkins显示html样式问题的几种解决方案总结

    前言 jenkins上使用HTML Publisher plugin插件生成的html报告样式会丢失,需要设置下才能正常显示. 一.样式丢失 1.官方文档的解释如下,参考地址https://stack ...

  7. 2_bootstrap的环境搭建

    2.bootstrap环境搭建 2.1.下载资源 中文官网地址:http://d.bootcss.com/bootstrap-3.3.5.zip http://www.bootcss.com 2.2. ...

  8. mysql之SQLYog配置

    SQLyog(MySQL图形化开发工具) 安装: 提供的SQLyog软件为免安装版,可直接使用 使用: 输入用户名.密码,点击连接按钮,进行访问MySQL数据库进行操作

  9. OpenCL 矢量存取

    ▶ 函数 vloadn 和 vstoren 来实现全局存储器和局部存储器之间的向量拷贝 ● 代码 #include <stdio.h> #include <stdlib.h> ...

  10. [jOOQ中文]2. jOOQ与Spring和Druid整合

    https://segmentfault.com/a/1190000010496053 jOOQ和Spring很容易整合. 在这个例子中,我们将整合: Alibaba Druid(但您也可以使用其他连 ...