Sep 21

从Java 平台计算XPath 不指定

Heck , 14:04 , 编程杂谈 , 评论(0) , 引用(0) , 阅读(749) , Via 本站原创 | |
分组 XML 技术

很多年前,当 XML 成为最热门的技术之一时,很难想象得到 XML 会在十年内变得如此重要,而且您可以找到大量与 XML 相关的有趣技术。实际上,可以通过多种途径了解 XML 技术。

传统的 XML 分组

可以将与 XML 相关的技术划分为一些基本的分组或主要区域:

   1. 文档创作:此分组面向将主要的时间用于创作 XML 的人。无论他们是创建原始 XML 数据,还是用 XML 格式表示现有数据,这里主要关注的都是纯 XML,很少关注可能使用这些文档的编程任务。这就是 XML 的核心思想所在,这个分组还包括特定的 XML 词汇表(比如 MathML)或一些科学 XML 词汇表。
   2. 处理 XML:这些技术(比如 XSL)允许对 XML 进行转换、修改或由一种格式迁移到另一种格式。这些技术主要关注的仍然是 XML 文档及其内部数据,但有时候也使用编程语言来完成这些转换。
   3. 读取/编写 XML(以及持久化数据):这些技术与编程联系更加紧密,包括从低级的 API(比如 SAX 和 DOM)到数据处理技术(比如 JAXB 和 Castor)。这些技术将 XML 看作一种数据存储机制,而且在许多情形下看作一种达到目的的手段。

到目前为止,这些分组是主要的技术类别,每个分组中都在不断出现最新的技术和规范。

将 XML 转变为一类数据居民

XML 的一个主要问题就是缺乏良好的搜索支持,尽管这个问题仅限于上面的三个分组。如果想要对 XML 格式的数据进行搜索,则这会是一个问题。实际上,通常的解决方案是将上面的一些分组结合起来。文档创作者可能使用命令行工具(比如 grep,这是一种笨拙的搜索方式。编程人员可能读入 XML(另一个分组),然后使用他们的编程语言(比如 Java 或 C#)对非 XML 格式的数据进行搜索。这种方法是可行的,但暴露了 XML 的一些局限性。

幸运的是,XPath(和 XQuery,本文稍后将会谈到)的引入和流行带来了一个新分组:

   4. 搜索 XML:XPath 和 XQuery 就属于这个分组。这些规范/技术允许以 XML 的方式对 XML 文档进行搜索。换句话说,可以使用 XML 语义进行搜索,并且不但能够搜索 XML 文档中的数据,还能够搜索这些文档的结构。

借助 XPath 和 XQuery,不再需要将数据由 XML 转换为一种编程语言,然后使用这种语言的工具来搜索数据。使用这种方法不仅会受到编程语言的约束,通常还会丢失大多数 XML 语义和结构(比如元素之间的父子关系等等)。使用 XPath 和 XQuery,无需编程语言 就可以对 XML 进行搜索。


尽管如此,仍然需要使用 Java(和其他)语言进行编程并与 XPath 和 XQuery 交互。尽管 XPath 和 XQuery 具有强大的 XML 搜索功能,编程人员仍然需要一种方式来使用这些技术。使用系统命令(比如 exec())来启动命令行进程是个很痛苦的过程,而且会引起您无法处理的错误。更糟的是,几乎不能对搜索结果进行处理。因此 XPath 必须与 Java(或 C# 或 Perl)语言结合起来。本文使用的是 Java 语言。(如果您想看看关于其他语言的文章,请在本文反馈部分中告诉我们!)

要阅读本文,至少应该熟悉 XPath。如果从未使用过此技术,请参阅 参考资料 中的链接,学习一个包含两部分的介绍性 XPath 教程,然后再阅读本文。

在这一点上(XPath 和 Java 技术的结合),Sun 为 Java 编程人员提供了很大帮助。Java 编程人员将 XPath 支持集成到了 Java 5 环境中。更好的是,您无需下载企业版本或者补充的程序包(比如 Sun 用来处理 JDBC 的某些部分的程序包)。如果计算机上已经安装了 Java 5 软件,那么您已经通过专注于 Java 的方式获得了对 XPath 的支持。实际上,您可能已经熟悉了 JAXP(Java API for XML Processing)的某些部分。

确保已经获得了 Java 5 发行版

如果您不能确定拥有的是哪个版本的 Java 技术,或者计算机(可能是远程服务器)上运行的是哪个版本的 Java 技术,那么您可以轻松地找出来。只需使用 -version 标志运行 java。清单 1 显示了应该看到的结果。


清单 1. 确保已经获得了 Java 5 或更高版本

                
[bdm0509:~/Documents/developerworks/java_xpath] java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)

只要版本是 1.5 或更高版本,您就能够顺利完成本文。注意,1.5 相当于 5.0,(我和大多数 Java 开发人员)都不太明白为什么所有公开著作上都称之为 “Java 5”,但是上面的 java 命令仍然会返回 1.5。如果您拥有 1.5.x 或者甚至是 1.6.x,那就再好不过了 。否则,请访问 参考资料 中的链接,下载 Java 5 技术。

确保获得了 XPath 支持

接下来,需要确保拥有 XPath 支持。这听起来有点多余;不就是检查一下是否获得了 Java 5 或更高版本的软件吗?尽管如此,仍然有很多开发人员在其系统上使用的 Java 版本与其开发环境所使用的版本不一样。或者 Eclipse(您的 IDE)运行的不是 Web 应用服务器等等……避免此类问题的最好方法是构建一个小程序来对软件进行测试。清单 2 展示了一个程序,该程序仅仅创建 XPath 工厂的一个新实例 XPathFactory。这也可以确保解析器和一个实现已经设置并能够运行。


清单 2. 一个非常简单的 XPath 测试类




编译这个类并运行它。您应该会获得如清单 3 所示的非常基本的输出。


清单 3. 清单 2 中的测试类的成功输出

                
[bdm0509:~/Documents/developerworks/java_xpath] java XPathTester
Successfully loaded XPath factory. Things look good.

这非常繁琐,但是您可以尝试在 Web 服务器、应用服务器、镜像生产服务器等想要运行 XPath 代码的地方使用它。如果这个类能够在这些机器上运行,那么您就能够安全地开发更加复杂的 XPath 应用程序。如果测试类不能运行,那么请在编写代码之前花点时间获得 XPath 支持,否则编写的代码可能不会正常运行。



XPath API 概述

理解 JAXP API 的 XPath 部分完全在于理解 JAXP 如何处理所有 的 XML 解析、处理和转换。

基础的 JAXP 工作流

以下是处理 XML 的基本步骤:

   1. 获得一个工厂类,以提供特定于供应商的 JAXP 实现的一个实例。
   2. 从工厂获得一个解析器或转换器。
   3. 设置关于这个解析器或转换器的配置选项(验证、名称空间感知、要使用的样式表等等)。
   4. 创建一个对象来持有、存储或引用要操作的 XML(通常通过一些类型的 InputSource 来实现)。
   5. 解析或转换 XML。

这些代码通常与清单 4 类似,其中展示了一个简单的 XML 解析,使用命令行参数作为要解析的 XML 文档的文件名。


清单 4. 使用 SAXParserFactory

                


如果构建一个 DOM 树,构建过程仍然遵循相同的模型。清单 5 展示了创建 XML 文档的 DOM 树的代码,步骤与清单 4 非常相似,但其中的类和方法名称发生了变化。

清单 5. 使用文档构建器工厂

                


在两种情形中,您都获得一个工厂,并用其创建一个新的解析器/处理器实例,然后在该实例上进行操作。

XPath 工作流

该工作流与您编写 XPath 代码时的流程非常相似:

   1. 获得一个 XPath 工厂,以提供特定于供应商的 XPath 实例的一个实现。
   2. 从工厂获得一个 XPath 计算器实例。
   3. 创建一个新的 XPath 表达式。(虽然这一步仍然与 XML 转换模型中的样式表分配保持一致,但它与解析模型不同)。
   4. 构建 XML 文档的一个 DOM 树,用于计算 XPath 表达式。
   5. 计算 XPath 表达式。

让我们逐步研究一下这个过程,构建一个基本的程序来解析 XPath 表达式,然后可以计算您自己的任何 XPath 或在学习 XPath 教程(参见 参考资料 获得其链接)过程中编写的任何 XPath。

为 XPath 提供一个 DOM 树

您需要记住用于本文将要构建的程序的一些假设:

    * 您有一个 XML 文档,而且可以轻松将其转换为 DOM 树。本文的示例从命令行读入一个 XML 文档,并将其转换为一个 DOM 树,但是您可以从网络 URL、一组 SAX 事件或任何其他来源轻松构建一个 DOM 树。如果您对如何使用 JAXP 从各种来源构建 DOM 树有些陌生,那么请访问 参考资料 中的一些有帮助的链接。
    * 需要计算 XPath。 本文假设您已经有一个 XPath,或者至少知道如何构建一个 XPath。本文不会详细讨论如何构建 XPath,而主要讨论如何计算它们。

满足这些假设之后,您就可以编写代码了。

获得一个要针对其计算 XPath 的 XML 文档

首先从一个简单的程序开始,该程序从命令行读入一个文件名。您将使用该名称从它引用的 XML 文档构建一个 DOM 树。此处的内容并不是特定于 XPath 或者甚至 JAXP 的;只是一些简单的 I/O 和程序片段。清单 6 是最初的程序;将其保存为 XPathEvaluator.java。

清单 6. 计算 XPath 的程序的最初版本

                


将 XML 转换为 DOM 树

XPath API(至少其当前的 JAXP 形式)需要由 DOM 树来操作。所有 XPath 都需要由某种类型的内存中模型来操作,因为 XPath 基本上与 XML 文档的层次结构相关。DOM 提供了一个这样的模型,一个可导航的元素、属性和文本节点树。

因为您已经使用 JAXP 来提供 XPath 支持,所以您也免费获得了 DOM 支持。使用 DocumentBuilder 类(及其相关联的工厂 DocumentBuilderFactory),以将字符串引用转换为一个内存中 DOM 树。清单 7 展示了用于实现此目的的 XPathEvaluator 类的附加部分。

清单 7. 从输入 XML 文档创建 DOM 树

                


大多数代码仅仅是基于 DOM 的 JAXP 解析;如果您还不太明白此处发生了什么,请参见 参考资料 获取关于一般的 JAXP 解析和转换的文章的链接。

关于名称空间感知的注意事项

正如大多数现代的和目前的 XML 规范所描述的,XPath 是名称空间感知的。这意味着元素的名称空间前缀(比如 iTunes:artist)可以作为 XPath 的一部分。即使您未 使用具有名称空间的文档,您也应该确保今后会拥有这种功能。

为此,您必须确保 DOM 树是名称空间感知的。换句话讲,您应该确保 XPath 计算的输入 是名称空间感知的,从而使您的计算也具有名称空间感知特性。要确保这一点,应该在构建 DOM 树时始终打开名称空间感知。清单 8 展示了为了实现此目的而新增的一行代码。

清单 8. 将名称空间感知添加到 DOM 树的构建过程中

                


在 Java 环境中表示 XPath

一旦拥有了要计算的 DOM 树,就需要获得 XPath(一个文本字符串)并为其创建一个 Java 表示。当然,这不仅仅意味着创建一个 String 变量并将 XPath 填充到其中。您需要一个实际的 Java 对象,它必须能够针对 DOM 树计算其本身,要么能够被其他某个 XPath 感知的组件计算。JAXP 的新 API 附加功能正好能在这里派上用场。

开始使用 XPath 工厂

前面提到的事件序列将在这里登上舞台。从一个新类 javax.xml.xpath.XPathFactory 开始所有的 XPath 工作。但获取 DOM 除外,从技术上讲,这可以在实际的 XPath 计算之前的任何时刻完成。

具体来讲,XPathFactory 是一个接口,您需要该接口的一个实现。这个实现将特定于供应商;Sun 提供了一个默认实现,Apache 可能有一个实现,Oracle 可能也有一个实现……但所有这些代码都与供应商密切相关。但是,您可以使用 XPathFactory 及其 newInstance() 方法(该方法为您获取一个 XPathFactory 实现)对供应商细节进行抽象。

清单 9 主要实现这个目的。注意,这个清单仅仅 展示了 evaluateXPath() 方法。您将需要将一些 import 语句添加到代码中来使其生效,需要添加的所有代码都位于 javax.xml.xpath 包中。


清单 9. 获取一个 XPathFactory 实例


获取一个 XPath 对象

接下来,您需要一个 XPath 对象。这个对象能够计算 XPath,并且是 XPath 感知的 Java 程序的基础。就像从 DocumentBuilderFactory 中获得一个 DocumentBuilder 一样,您从 XPathFactory 获得一个 XPath。清单 10 展示了这段代码。


清单 10. 获取一个 XPath 对象



有了这个类,您就可以计算 XPath 并处理结果了。

计算 XPath 表达式

一旦有了一个 XPath 实例,您就可以计算 XPath,获取一个结果节点集并对结果进行一些处理。

计算 XPath

在 XPath Java 对象上使用 evaluate 方法计算 XPath(不是 Java 对象,而是引用 XML 文档的一个字符串路径)。这有点容易混淆:您使用 XPath 计算 XPath。因此,从实际的角度上讲,XPath 对象是一个 XPath 计算器。

evaluate() 方法拥有两个参数:一个字符串 XPath,一个 DOM 树(用来针对其计算该 XPath),还有一个 XPath 常量表示返回类型。事实证明返回类型很不灵活;返回类型的规范化只是为了未来的兼容性;现在,始终使用 XPathConstants.NODESET,以将结果作为一个 DOM NodeList 结构返回。

查看清单 11 中用于计算 XPath 的代码,将其添加到 evaluateXPath 方法中。

清单 11. 计算 XPath



清单 11 包含一些新特性,这些特性都非常重要:

   1. 该方法现在返回一个 org.w3c.dom.NodeList。确保将一个 import org.w3c.dom.NodeList; 语句添加到代码中,以使其生效。NodeList 是一个结构,用于从 XPath 计算中返回节点列表。
   2. 整个代码块包装在一个 try/catch 块中,并且由 XPath 计算导致的异常(javax.xml.xpath.XPathExpressionException)被捕获并作为一个 IOException 再次抛出。您立刻就会明白这背后的原因。
   3. 使用传递给该方法的 XPath 字符串调用 evaluate(),在类的构造函数中构建的 DOM 树和常量指示将结果作为一组节点返回。
   4. evaluate 的结果(一个 Object)被转换为 DOM NodeList 类型并返回。

尽管发生了很多事情,但它们都非常直观,应该不会出错。

特定于 XPath、特定于 DOM、特定于 JAXP?

一个有趣的事情是决定将来自此方法的任何异常,以及构造函数中出现的任何异常作为 IOException 返回。这是一个设计决策,实际上并非特定于 XPath,但是这个 决策非常重要。有了这个决策,您可以通过命令行或另一个程序使这个类的用户不用了解、导入或直接使用任何 XPath 类或接口。

实际上,您从 DOM 抽象出所有 JAXP 类、DOM 类、SAX 类以及 XPath 类……但 NodeList 类除外。这项功能非常强大,因为其他编程人员无需熟悉 JAXP 或 XPath API 就可以进行 XPath 计算。它将您的程序从有趣的编程体验转变为一个可重用的工具,这是一个非常重要的特性。

如果您采用这个原则并想更进一步,您可以获取返回的 NodeList 并对其进行迭代,将结果转储到一个 Java List 中。这将完全抽象出 DOM 的细节,然后删除当前对 org.w3c.dom.NodeList 的所有依赖关系。

处理计算结果

获得了 XPath 计算的结果之后,就可以对结果进行处理了……无论怎么处理都行。例如,您可以只是在结果中进行迭代并将它们打印出来。当然,您可以进行更加复杂的处理。

结果节点上的一个非常简单的迭代

实际上,NodeList 的每个成员都是一个 DOM Node(org.w3c.dom.Node),并且您可以找到每个节点的名称和类型,甚至关于节点的任何内容。清单 12 展示了 XPathEvaluator 类的一个非常基本的附加功能,它传入一个 XPath 以进行计算,获得结果并将其打印出来。

清单 12. 完成 XPathEvaluator 程序

                


如果您阅读了关于 XPath 的两部分教程,那么您将会在选择一些属性时感受到 XPath 的强大功能。如果还没有示例文件 xerces-build.xml,那么您应该下载(参见 参考资料)并运行此示例,如清单 13 所示。

清单 13. 运行 XPathEvaluator 程序
                
[bdm0509:~/java_xpath] java ibm.dw.xpath.XPathEvaluator xerces-build.xml
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property

这些结果非常单调,与图 1 比较时更是如此,图 1 是来自一个教程的屏幕截图,该教程使用计算 XPath 的工具对这个表达式进行图形化计算。

图 1. 用图形化工具计算 XPath 表达式
您可以在谓词中使用 starts-with() 并指定比较中涉及到的任何相关节点
点击在新窗口中浏览此图片
但是,打印出的元素视图非常 简单。

一个拥有多个名称的节点

请记住,尽管示例程序的所有工作只是打印节点名称、类型,以及可能的值(依赖于节点类型),您仍然会获得一个完整的 Node 对象。而且,这个节点并不是孤立的;它是对内存中的 DOM 树的一个引用(您可能未从使用透视图中看到这个 DOM 树,它隐藏在 XPathEvalutor 节点内部)。

这意味着,对于每个 Node,您实际上获得了传递给 XPathEvaluator 的完整 XML 文档中的一个位置指针。这意味着您可以导航到节点的子节点,查看每个元素节点的属性,找到文本节点的父元素的名称,以及在 Node 上执行任何其他允许的 DOM 操作。您不仅仅 是拥有一个节点,您拥有该节点在完整的 DOM 上下文中的引用。您可以决定对该节点和它所处的上下文进行何种操作。

关于早期的 JAXP、DOM 和 XPath 抽象……

您可能已经注意到,上面讨论的用于避免特定于 DOM 的引用的所有工作现在都显得不重要了。实际上,这就是 XPathEvaluator 从类的用户那里抽象出 XPath 细节,但仍然返回一个 DOM NodeList 的原因。您可以使用户安全地脱离 JAXP 和 XPath,将主要精力放在 XPath 计算的结果上,您将需要对 DOM 进行处理。

出于此原因,最好返回 DOM 结构,但是要避免对特定于 XPath 的输入的需求,或者避免提供特定于 XPath 的输出。让您的用户仅仅处理 DOM,至少在类函数需求方面不需要任何其他内容。


XQuery

开发人员的性格都比较急躁。当您开始获得从 Java 环境使用 XPath 的感觉和命令时,您可能已经在想 Xpath 还不能 进行哪些操作。数据之间非常复杂的关系不易处理(使用类似于 SQL 的连接已经达到 XPath 的功能上限了),您必须在 Java 环境中对节点进行排序和过滤,而且如果还不熟悉规范的话,将很难读懂 XPath。

幸运的是,您可以很自然地从 XPath 转向另一种能够解决所有这些局限性的语言,通过一种能够记住已经完成的操作的方式来完成。XQuery 添加了一个更加 XML 化的 SQL 版本,允许构建查询、对结果进行排序,并在查询中使用实际的 WHERE 语句。XQuery 也构建在 XPath 之上,这意味着您所知道的关于节点、谓词匹配,以及元素和属性如何彼此关联等所有内容都适用于 XQuery。

而且,就像 XPath 一样,XQuery 拥有一个面向 Java 编程人员的 API:XQuery for Java (XQJ) API。要了解关于 XQuery 的更多信息,请参见 参考资料,其中包含关于 XQuery 和 XQJ 的文章和教程的链接。如果觉得已经对 XPath 融汇贯通了,那么可以考虑使用 XQuery 来向 XML 相关的应用程序代码添加更强大的功能。


结束语

从 Java 技术的角度使用 XPath 主要包括学习新语法、配置一个 API 和一些工具,然后应用所掌握的 XPath 技术。但是您不应该认为在 Java 环境中使用 XPath 是件微不足道的事情。除了复杂性之外,在通过 Java 编程来处理 XML 时,XPath 还提供了的巨大的灵活性。它能够实现的功能远远超出了大多数基本的 SAX、DOM、JAXP、JDOM 或其他实现所提供的功能(尽管一些供应商和项目为这些规范和 API 所具有的基本功能提供了支持 XPath 的扩展)。

而且,XPath 还能够使用更加复杂的 XQuery 语言,(通过 XQJ API)结合使用 Java 和 XQuery。无需立即转向 XQuery,您将会对 XPath 技能更加融汇贯通,学习从 Java 应用程序内部选择复杂的节点集,并根据需要对其进行操作。您将会发现很多情况下只需要 XPath 就能够处理。另外,XQuery 构建在 XPath 之上(从语法的观点和 XQJ API 方面来讲,二者都能够实际地计算并执行 XQuery),所以您不知不觉中就提高了 XQuery 技能。最重要的是,能够体会到 XPath 所提供的强大灵活性所带来的乐趣,从 Java 环境进行计算时更是如此。


来源:Heck's Blog
地址:http://www.hecks.tk/java-calc-xpath-xml/
转载时须以链接形式注明作者和原始出处及本声明,否则将追究法律责任,谢谢配合!
Tags: , ,