XML 是 Web 的 SGML,但是它还没有像 XML 社区那样在 Web 上引人注目。XML 在 Web 上最突出的成就 —— XHTML —— 已经被政治和委员会设计所纠缠,并且其他雄心壮志、技术良好的规范 —— 例如 XForms 和 SVG —— 一直受到低使用率的困扰。有时候 XML 会在意想不到的方面在 Web 上获得成功,包括 XML 格式的 Web 提要(例如 RSS 类型和 Atom)的流行。
常用缩略词
和其他 Web 上的技术一样,Web 上的 XML 以浏览器为中心,但是大部分关于在 Web 上处理 XML 的讨论都集中在服务器端。在 developerWorks 的 Firefox and XML 系列(参见 )中,我介绍了几种在 Firefox 浏览器中使用 XML 的方法。遗憾的是,跨浏览器处理 XML 甚至比跨浏览器处理 HTML 更加奇怪,这就是为什么这么多 Web 上的 XML 处理坚持相对安全的服务器端领域的部分原因。
许多动态 HTML 开发人员厌烦了跨浏览器的痛苦和浏览器之间脚本编写的怪癖 。几种出色的 JavaScript 库的出现使开发人员的工作更加轻松。这些库中最流行一种就是 jQuery,developerWorks 上有几篇文章已经对它进行了介绍。如果您知道如何绕开这些巨大的陷阱,您还可以使用 jQuery 来处理 XML。在本文中,我将展示如何在实际场景中联合使用 jQuery 和 XML,如何使用 Atom Web 提要,介绍一种在 jQuery 中处理 XML 的实用模式,并解决不幸遇到的实际问题。您需要对 XML、XML 名称空间、HTML、JavaScript 和 jQuery 库有基本的了解(请参阅 了解更多介绍 jQuery 的文章)。
XML 名称空间的问题我将首先介绍最严重的问题。jQuery 并不能完全解决 XML 名称空间问题。这个众所周知的问题由来已久,人们尝试了各种解决方案,但结果都不太令人满意。理想的解决方案可能是利用 jQuery 支持 CSS Level 3 名称空间选择器(仍然是一个 W3C 工作草案,请参阅 ),它将添加一个新的选择器,如下所示:
@namespace ex url(); ex|quote { font-weight: bold }
第一行是 名称空间的前缀声明,第二行是一种使用新的名称空间组件的类型选择器,其中用竖线符号分隔已声明的前缀和本地名称。不幸的是,jQuery 并不支持这种方法,因此人们采取了各种方法来处理名称空间问题。
伪装前缀的重要性一种最常见的黑客方法是在 jQuery 中处理 XML 和名称空间时忽略名称空间,并选择完整的 qname(前缀和本地部分)。
$(xml).find("x\\:quote").each(function() { //process each node });
该代码通过 jQuery 的节点名称概念选择,即 DOM nodeName 属性。它包含一个冒号,是 jQuery 选择器保留的符号,并且必须使用反斜杠进行转义。反斜杠是 JavaScript 脚本保留的符号并且必须是一对。这种黑客方法在使用不同前缀的名称空间等效文档中无法使用。
使用属性过滤器据说有人成功地使用过以下方法的变体,即在伪属性 nodeName 上使用 jQuery 属性过滤器:
$(xml).find("[nodeName=x:quote]").each(function() { //process each node });
使用 jQuery 1.3.x 之前的版本,您需要在 nodeName 前面加上 @。但是,这样做与上一节 中提到的方法有着相同的基本问题。它将破坏许多真实的名称空间场景。我尝试了以下变体,这种方法更合理:
$(xml).find("[namespaceURI='http://example.com'][localName='quote']") .each(function() { //process each node });
可惜这样不起作用。
寻找一个好的插件这种混乱不完全是 jQuery 的错。DOM 为寻找节点提供了有效的方法:getElementsByTagName 和 getElementsByTagNameNS。后者旨在感知名称空间,接受名称空间的 URI 并忽略前缀,但遗憾的是,尽管其他浏览器都支持它,但 Microsoft® Internet Explorer® 除外。然而,jQuery 的目的是处理此类浏览器问题,以便消除人们的此类烦恼。一种可能的、牵强的理由是,jQuery 很大程度上以 CSS 作为其选择器的基础,并且即使是 W3C CSS Level 3 名称空间选择器也无法使它通过工作草案阶段。jQuery bug #155,“Get Namespaced Elements in XML Documents”(参见 ),涵盖了这些问题,但是问题在 3 年之内没有得到解决。
Ryan Kelly 遇到此问题并做了一次大胆的尝试,为 XML Namespace Selector 创建了一个 jQuery 插件 jquery.xmlns.js(参见 )。它试图支持以下代码。
$.xmlns["ex"] = "http://example.com"; $(doc).find("ex|quote").each(...);
第一行是对该插件的全局名称空间声明 — 由于底层 jQuery 机制的局限性。它的确用典型的 jQuery 用语为名称空间范围提供一个非全局块。 遗憾的是,我在使用这种扩展时成败参半。我希望它能够改变,并最终找到合适的方法进入 jQuery 。
一个更简单的插件我最终选择的解决方案是创建一个简单插件,它不使用 jQuery 选择器做任何特殊工作,而是添加一个新的过滤器。您可以直接传递一个名称空间和本地名称到该过滤器,从而使结果集与节点匹配。请您按以下方法使用它:
$(xml).find('*').ns_filter('http://example.com', 'quote').each(function(){ .each(function() { //process each node });
ns_filter 是我写的特殊过滤器。执行一个单独的 find('*') 的需求看起来可能不优雅,更简单的变化可能是:
$(xml).find('quote').ns_filter('http://example.com').each(function(){ .each(function() { //process each node });
然而,这样做并不可行,因为您不能相信 jQuery 能够以名称空间中立(即作为本地名称选择器)的方式来处理查询,例如 find('quote')。我的过滤器实现将在下一节提供,作为安装 jQuery 来处理 XML 的一般系统的一部分。我在 Mac OS X Snow Leopard 操作系统下的 Firefox 3.5.5 和 Safari 4.0.4 ,以及 Windows® XP 操作系统最新的 Internet Explore 7 和 Internet Explorer 8 浏览器中对它进行了测试。
jQuery XML 工作台名称空间问题只是以下事实的症状:说到底,jQuery 是一个 HTML 工具。我发现,使用 jQuery 处理 XML 最实用的方式就是为 XML 文档创建一个 HTML 工作台,通过可靠地跨浏览器方法引用脚本,然后建立需要的暂时性解决方案,例如针对 XML 名称空间问题的解决方案。您可以用工作台模式准备并测试您基于浏览器的 XML 处理的模式和技术,您甚至还可以把工作台作为基于浏览器的应用程序本身的基础。
(quotes.html)是 HTML 使用工作台的简单例子。它能够动态地从 XML 文件加载引用。
清单 1 (quotes.html). 使用 jQuery XML 工作台的 HTML 例子