内容


利用 XPath-jQuery 集锦手册在 XPath 和 jQuery 之间做选择

二者访问结构化数据的功能都很强大,随心所欲地选用它们

Comments

简介

XML 是一种受到良好支持的 Internet 标准,使用它编码的结构化数据很容易被几乎任何编程语言解码,甚至能够被人类阅读或编写,只要他们使用标准文本编辑器即可。很多应用程序,尤其是现代的标准兼容的 Web 浏览器,都可以直接处理 XML 数据。

XPath(XML Path 语言)是一种强大的查询语言,用于选择 XML 文档中的节点。XPath 标准的 1.0 版本广泛实现于各种语言,比如 Java™、C# 和 JavaScript。

jQuery 是一种事实上标准的跨浏览器 JavaScript 库,用于选择和操纵 XHTML 文档(以及通过 Ajax 加载的 XML 文档)中的节点。它已经被很多大公司(包括 Google、IBM®、Microsoft® 和 Twitter)采用。在我写作本文时,发布了它的当前版本 1.4;所以我马上进行了升级,以利用它所承诺的更快速度。注意,本文中的 jQuery 例子应该未加修改地使用前一版本,即 jQuery 1.3.2。

为何在 XPath 存在于 JavaScript 中时使用 jQuery?

如果 XPath 是 W3C 标准,并且实现于 JavaScript 中,那么为什么还要麻烦地使用 jQuery 呢?

XPath 是一个普通的 XML 标准,而 jQuery 是一个轻量级的库,设计用于处理跨浏览器兼容性方面的问题,使您不必担心用户运行的是哪种浏览器。它足够灵活,可以在使用标准 JavaScript 词汇的浏览器 DOM 中工作,它还提供一些大大简化 Web 应用程序开发的附加特性,比如强大的 Ajax 和动画支持。

但是,应该总是对手边的工作使用适当的工具;更多地了解这两个工具无疑有助于您为下一个项目挑选适当的技术。

例子

整篇文章中,都会回过头来参考一个方便的样例 XML 文档,参见 清单 1。此书籍列表包括各种信息,比如作者、两种完全虚构的价格和书名。

清单 1. 样例 XML 文档 (book.xml)
<?xml version="1.0" encoding="utf-8"?>
<catalog>
    <book format="trade">
        <name>Jennifer Government</name>
        <author>Max Barry</author>
        <price curr="CAD">15.00</price>
        <price curr="USD">12.00</price>
    </book>

    <book format="textbook">
        <name>Unity Game Development Essentials</name>
        <author>Will Goldstone</author>
        <price curr="CAD">52.00</price>
        <price curr="USD">45.00</price>
    </book>

    <book format="textbook">
        <name>UNIX Visual QuickPro</name>
        <author>Chris Herborth</author>
        <price curr="CAD">15.00</price>
        <price curr="USD">10.00</price>
    </book>
</catalog>

注意,我跟这里列出的作者和发行人没有任何联系,只有最后那个明显的除外。价格完全是瞎编的,至于实际的定价,您应该询问自己喜欢的书店。

XPath 假定

对于本文中的 XPath 代码,您要做以下假定:

  • 已经将 book.xml 文件(参见 清单 1)加载到您的 XPath 实现可以使用的格式。
  • 从一个表示文档根的对象开始搜索。就是以 <catalog> 元素作为子元素的那个对象。您将称之为 root,因为它是 XML 文档层次结构的根。

由于在太多不同的平台上有太多的 XPath 实现,所以下面我们将重点放在 XPath 语句本身上,并使用一个类似于 JavaScript 的伪代码来在上下文中展示这些语句;请查看您喜欢的开发平台的类库,了解有关加载 XML 文档和您可用的特定 XML 节点对象的信息。

jQuery 假定

本文中的 jQuery 代码做以下假定:

  • 使用的是最新(版本 1.4.0)jQuery 代码(参见 参考资料 中的链接)。
  • 已经通过 jQuery.get()jQuery.post() 方法加载了 book.xml 文件,并且已经将最终的 XML 文档存储在一个名为 root 的变量中(与 XPath 例子相同)。

用于做这件事的一些样例代码在 清单 2 中。

清单 2. 利用 jQuery 加载 XML 样例
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
               "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Book Catalog</title>
<script type="text/javascript"
src="//ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js"></script>
<script type="text/javascript">// <![CDATA[
var root = null;

$(document).ready( function(){
    $.get( "http://localhost/~chrish/books.xml", 
        function( data ) {
            root = data;

            $("p#status").text( "Loaded." );
        } );
} );
// ]]></script>
</head>

<body>
<p id="status">
Loading book.xml...
</p>
</body>
</html>

$(document).ready() 函数中,使用 jQuery get() 方法从本地 Web 服务器加载 books.xml,将最终的文档对象存储在 root 变量中,并用 status ID 设置段落的文本,以表明 XML 已完成加载。有关 jQuery 的更多信息,请查看本文末尾 参考资料 中的相关链接列表。

选择节点

XPath 和 jQuery 的基本目的都是从文档选择节点。一旦选择了一个节点(或一个节点集合),就可以找到您正在寻找的数据以及在需要时操纵文档。

XPath 设计用于确切返回您所寻找的节点;它一般非常特定。另一方面,jQuery 则使得操作大型节点集合非常容易,所以有时候您必须在开始处理节点之前小心地缩小匹配范围。

按名称选择节点

在搜索特定节点时,您通常知道它的名称,或者知道其父元素的名称。

要找到一个特定的元素,您会使用它的名称,如 清单 3 所示。

清单 3. 按名称选择节点
/* Find all <book> elements through XPath: */
var result = root.find( "//book" );

/* Find all <book> elements through jQuery: */
var result = $(root).find( "book" );

用于选择所有 <book> 元素的 XPath 语句(//book)使用两个斜杠 (//),来指定从当前节点(本例中是 root)开始、所有匹配的节点都会被找到。这是 jQuery 的默认行为,所以您不需要包含任何其他内容。在两种情况下,结果都是 清单 1 中的所有三个 <book> 元素。

通常可以通过指定元素路径来缩小搜索结果;结果将是自路径末尾的匹配节点(参见 清单 4)。

清单 4. 按路径选择节点—两者行为不同
/* Be more specific (XPath): */
var result = root.find( "/catalog//book" );

/* Be more specific (jQuery): */
var result = $(root).find( "catalog book" );

从根元素 (/) 开始,这个 XPath 语句将寻找第一个 <catalog> 元素,然后返回这第一个 <catalog> 元素下的所有 <book> 元素。jQuery 语句的行为稍有不同;它将返回所有 <catalog> 元素下的所有 <book> 元素(参见 清单 5)。对于例子 book.xml 文件,结果是相同的节点集,但是如果您想要得到 <book> 元素下的所有 <author> 元素,那会怎么样呢?跟 清单 3 中一样,应该以两个斜杠 (//) 开始 XPath 表达式。

清单 5. 按路径找出内含的节点—两个例子的行为一样
/* Get all authors from all books (XPath): */
var result = root.find( "//book//author" );

/* Get all authors from all books (jQuery): */
var result = $(root).find( "book author" );

要让 jQuery 跟 清单 4 中的 XPath 样例一样返回第一个 <catalog> 中的 <book> 元素,您必须指导它使用它找到的第一个 <catalog>(参见 清单 6)。

清单 6. 匹配第一个 catalog 中的 book—这些例子的行为一样
/* All books from the first catalog (XPath): */
var result = root.find( "/catalog//book" );

/* All books from the first catalog (jQuery): */
var result = $(root).find( "catalog:first book" );

找到元素的最后一次出现(比如项目列表中的最后一个列表项,或者选择列表中的最后一个选项)也是一个常见操作。要正确地将一些东西附加到列表末尾,需要知道此末尾的位置(参见 清单 7)。

清单 7. 找到 catalog 中的最后一个 book
/* The last book from the first catalog (XPath): */
var result = root.find( "/catalog/book[last()]" );

/* The last book from the first catalog (jQuery): */
var result = $(root).find( "catalog:first book:last" );

在两种情况下,您都得到第一个 <catalog> 元素中的最后一个 <book> 元素,这正是您在寻找的。在 XPath 例子中,last() 函数返回最后匹配元素的索引(用在方括号中)。

选择任意节点

有时,您不知道您在寻找的元素的名称,或者需要找到一个可能在几个不同元素中的元素。在 XPath 和 jQuery 中,都可以使用星号 (*) 来匹配任意元素(参见 清单 8)。

清单 8. any 元素
/* Find all authors in all elements inside of <catalog> (XPath): */
var result = root.find( "/catalog//*//author" );

/* Find all authors in all elements inside of <catalog> (jQuery): */
var result = $(root).find( "catalog:first * author" );

注意,我在 jQuery 样例中使用了 :first,以使它的行为完全跟 XPath 版本一样。

按属性选择节点

类似的元素通常具有独特的属性,比如 XHTML 元素中使用的 id 属性,是为了给 XHTML 元素一个惟一的引用 ID(参见 清单 9)。有时,您并不太关心特定的元素,而是关心一个属性为特定值的元素。

清单 9. 找到那些讨厌的教科书
/* Find all books that are textbooks (XPath): */
var result = root.find( "//book[@format='textbook']" );

/* Find all books that are textbooks (jQuery): */
var result = $(root).find( "book[format='textbook']" );

两个例子都将返回所有具有 format 属性且属性值设置为 textbook 的 <book> 元素(清单 1 中的 book.xml 文件中有两个这样的元素)。XPath 的语法使用一个 @ 符号来匹配属性(jQuery 只是将属性括在方括号中),您需要包含两个斜杠 (//) 来匹配所有 <book> 元素,但是两个查询非常类似,都很直观。

针对 XHTML 中两个最常见的匹配属性(idclass),jQuery 包含了两个快捷方式。在 XPath 中,您必须显式地写出它们(参见 清单 10)。

清单 10. 基于 idclass 属性匹配 XHTML
/* Find the "status" <p>, then the highlighted elements (XPath) */
var result1 = xhtml_root.find( "//p[@id='status']" );
var result2 = xhtml_root.find( "//*[@class='highlight']" );

/* Find the "status" <p>, then the highlighted elements (jQuery) */
var result1 = $( "p#status" );
var result2 = $( ".highlight" );

假设您的 XHTML 文档是有效的(确实是的,对吧?),ID 匹配查询将只返回一个元素,因为在有效的 XML 文档中,ID 必须是惟一的。

如果您是层叠样式表(Cascading Style Sheets,CSS)的粉丝,可能注意到了,jQuery 选择器几乎跟 CSS 选择器完全相同。这很方便,因为您只需要记住一个针对通过 jQuery 找到想要的元素和利用 CSS 为元素定义样式的标准。

多个选择器

XPath 和 jQuery 都允许您组合多个选择器来检索每个匹配任意查询的节点(就是说,将得到结果的并集)。在 XPath 中,用竖线 (|) 字符组合语句,而在 jQuery 中则使用逗号 (,)(参见 清单 11)。

清单 11. 找到多个选择器的结果
/* Find all book names and all authors (XPath) */
var result = root.find("//name|//author" );

/* Find all book names and all authors (jQuery) */
var result = $(root).find( "name,author" );

在两种情况下,结果都是文档任何地方所有 <name> 和 <author> 元素的列表。在 图 1 中,使用 AquaPath 查看 XPath 结果(AquaPath 是一个用于 Mac OS X Tiger 的工具,有关它的更多信息,请参见 参考资料)。

图 1. XPath 结果,book.xml 文件中所有 book 的 name 和 author 标记都已突出显示
XPath 结果的屏幕截图,book.xml 文件中所有 book 的 name 和 author 标记都已突出显示
XPath 结果的屏幕截图,book.xml 文件中所有 book 的 name 和 author 标记都已突出显示

遍历节点

除了选择节点之外,通常还需要遍历文档结构,以便找到相关的数据或者执行复杂的操作。XPath 和 jQuery 都可以帮助您遍历文档。

根据前面学习的内容,您可以使用这些遍历方法来帮助找到祖先(即包含当前元素的元素)或子孙(即当前元素包含的元素)。

例如,清单 12 用于找到包含您找到的最后一个 <book> 的 <catalog>。

清单 12. 哪个 catalog 列出最后一个 book?
/* Find the catalog for the last book you know about (XPath) */
var result = root.find( "//book[last()]/ancestor::catalog" );

/* Find the catalog for the last book you know about (jQuery) */
var result = $(root).find( "book:last" ).closest( "catalog" );

图 2 显示了结果。

图 2. 最后一个 book 的 catalog 祖先
突出显示的 catalog 标记的屏幕截图,该标记是 book.xml 中最后一个 book 的 catalog 祖先
突出显示的 catalog 标记的屏幕截图,该标记是 book.xml 中最后一个 book 的 catalog 祖先

有一件事要注意,jQuery closest() 方法的行为很像 XPath 的 ancestor-or-self;如果匹配的话,它将包含当前节点。在本例中它不包含当前节点,但是如果您可以嵌套名称相同的元素或者是在匹配属性,那么应该牢记这件事。

如果需要朝另一个方向走,找到嵌入在您已经找到的元素中的元素,也是可以做到的(参见 清单 13)。

清单 13. 找到列出在 catalog 中的 price
/* Find the prices of everything in the catalog. (XPath) */
var result = root.find( "//catalog/descendant::price" );

/* Find the prices of everything in the catalog. (jQuery) */
var result = $(root).find( "catalog price" );

对于那些特殊情况,即选择的节点可能匹配您正在寻找的元素,那么跟 XPath 中的祖先一样,子孙也具有 descendant-or-self(参见 图 3)。

图 3. 所有 price,已选中
突出显示的 price 标记的屏幕截图,这是 book.xml 中列出的 book 中的 price
突出显示的 price 标记的屏幕截图,这是 book.xml 中列出的 book 中的 price

模拟高级 XPath 特性

XPath 指定了很多在 jQuery 中不必要的有用特性;毕竟,jQuery 运行在浏览器中,可以充分利用 JavaScript 的优势,而 XPath 通常用于比较受限的环境中,比如 XSLT 处理。

当然,只要您想用,这并不能阻止您用 JavaScript 实现这些特性。

很容易数清查询结果的个数(参见 清单 14)。

清单 14. 多少节点匹配选择器?
/* How many price entries do you have? (XPath) */
var result = root.find( "count(//price)" );

/* How many price entries do you have? (jQuery) */
var result = $(root).find( "price" ).length;

有时只需要知道节点是否包含某个字符串(参见 清单 15)。

清单 15. 第三个 <author> 中包含 Chris 吗?
/* Does the third <author> have "Chris" in its contents? (XPath) */
var result = root.find( "contains(//book[3]/author,'Chris')" );

/* Does the third <author> have "Chris" in its contents? (jQuery) */
var result = $(root).find( "book:eq(2) author:contains('Chris')" ).length > 0

清单 15 中有一个非常重要的区别需要注意,XPath 的索引从 1 开始,不是从 0 开始。在 jQuery 中,必须使用 :eq(2) 来得到第三个结果。

XPath 也有一个 sum() 函数,它以匹配节点的内容作为参数,将这些参数转换成数值,并返回这些值的和。在使用 jQuery 时,必须编写一个简短的函数来模拟该函数(参见 清单 16)。

清单 16. 计算一些节点内容的和
/* Sum the Canadian prices (XPath) */
var result = root.find( "sum(//price[@curr='CAD'])" );

/* Sum the Canadian prices (jQuery) */
function sum( root, selector ) {
    var x = 0;
    $(root).find( selector ).map( function() {
        if( this.text ) {
            // Internet Explorer-only
            return x += ( this.text * 1 );
        }

        // Firefox and W3C-compliant browsers
        return x += ( this.textContent * 1 );
    } );
    return x;
}

var result = sum( root, "price[curr='CAD']" );

jQuery 中的 map() 方法为每个结果节点运行指定的函数。注意,要得到结果节点的内容,也必须稍微费点事。确保在所有您喜欢的浏览器上测试这类 JavaScript。

您现在应该比较能理解何时以及如何将 XPath 1.0 和 jQuery 1.4 用于类似的任务了。

结束语

对于从格式良好的 XML 文档(包括 XHTML 页面)选择节点,XPath 和 jQuery 具有强大的查询语义。尽管它们的语法不同,但是无论使用哪一个来基于元素名或属性值从文档选择重要的或感兴趣的节点,都相当容易。

对于匹配与当前匹配的元素相关的元素节点,XPath 和 jQuery 都支持直观的遍历语义。此外,由于 jQuery 运行在完全的 JavaScript 解释器中,所以您可以用少量的代码模拟 XPath 的一些高级特性。


相关主题

  • 使用 jQuery 在浏览器中处理 XML(Uche Ogbuji,developerWorks,2009 年 12 月):了解如何利用 jQuery 在浏览器中直接处理 XML。
  • Java 语言的 XPath API(Elliotte Rusty Harold,developerWorks,2008 年 8 月):了解 Java XPath API。
  • 使用 jQuery 简化 Ajax 开发(Jesse Skinner,developerWorks,2007 年 4 月):学习 jQuery 理论,发现它的特性和函数,执行一些常见的 Ajax 任务,明白如何用插件扩展 jQuery。
  • XML Path Language (XPath)(developerWorks,2007 年 2 月):更多地了解 XPath。
  • XPath 教程(来自 w3schools.com):学习如何使用 XPath 在 XML 文档中找到所需的内容。
  • jQuery Tutorials 页面:这些教程中介绍了 jQuery 库的基本知识和更深入的主题,比如了解如何在 XHTML 页面中使用 jQuery。
  • The XML FAQ:探索 XML 信息的另一个优秀资源,即由 Peter Flynn 编辑的 XML FAQ。
  • XML DOM 教程(来自 W3schools.com):了解哪些基于 XML 的接口对浏览器可用(以及哪些浏览器支持它们)。
  • 本作者的更多文章(Chris Herborth,developerWorks,2006 年 3 月至今):阅读关于 XML 和其他技术的文章。
  • jQuery 版本 1.4.0:下载 jQuery 并用一个快速且简明的 JavaScript Library 加速您的 Web 开发,这个 JavaScript Library 可以简化 HTML 文档遍历、事件处理、动画和 Ajax 交互。
  • AquaPath:尝试 AquaPath(一个免费的基于 Cocoa 的开发者工具,适用于 Mac OS X Tiger 平台),针对任何 XML 文档计算 XPath 2.0 表达式,以及在动态、直观的树形表示中查看结果序列。
  • developerWorks XML 专区:在 XML 专区获取提高您的专业技能所需的资源。
  • developerWorks 技术活动网络广播:随时关注技术的最新进展。
  • IBM 产品评估试用版软件:下载或 在线试用 IBM SOA Sandbox,并开始使用来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
  • developerWorks on Twitter:立即加入,了解 developerWorks 上的活动信息。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=483770
ArticleTitle=利用 XPath-jQuery 集锦手册在 XPath 和 jQuery 之间做选择
publish-date=04192010