【Java精品源码栏目提醒】:网学会员鉴于大家对Java精品源码十分关注,论文会员在此为大家搜集整理了“【精品】XQuery高级应用开发应用程序惯用法 - 其它资料”一文,供大家参考学习
XQuery 高级应用:开发应用程序惯用法使用扩展函数、单元测试和断言、递归与排序以及高阶函数James R. Fuller 技术主管 FlameDigital Limited amp Webcomposite s.r.o.简介: XQuery 规范发布一年多了。
大量涌现的可靠的实现以及新的兴趣(如果根据开发人员闲聊的话题),似乎表明 XQuery 最终将得到更广泛的应用。
这也许是因为开发人员开始找到 XQuery 与其他多种 XML 技术(如 XML 数据库、XSLT、XML Schema)一起使用的办法了。
XQuery 已经超越了作为一种 XML 查询语言的最初定位,本文介绍如何将其用于开发中间件和 Web 应用程序。
标记本文!发布日期: 2008 年 12 月 22 日级别: 中级访问情况 2349 次浏览建议: 0 添加评论 平均分 (共 13 个评分 )开始之前讨论 XQuery 示例代码之前,先介绍一下如何学好本教程,如何安装和使用所附的源代码(参见下载 小节)。
关于本教程本教程介绍如何使用 XQuery 开发应用程序和中间件。
列举了在应用程序开发中 XQuery的一些局限,就如何克服这些局限提供了切实可行的建议,同时强调 XQuery 在处理这些问题时的优势和缺点在哪里。
然后在此基础上,利用大量的篇幅介绍了应用程序开发中一系列常见的编程惯用法: 使用扩展函数 单元测试与断言 递归与排序 高阶函数每一节都带有源代码示例(参阅 下载 小节)。
回页首目标本教程的目的是帮助您学习如何使用 XQuery 开发应用程序。
我将尽量提供可以剪切粘贴的成熟代码以便在您的应用程序开发中直接使用,但是我希望这些例子能够使您重新考虑XQuery 的应用,而不仅仅是查询 XML。
回页首先决条件本教程是为那些对 XML 技术有一定了解而对 XSLT 或 Query 略有涉猎的开发人员编写的。
这里提出的编程惯用法并不是我发明的,在多种计算机语言中都以各种形式存在。
这种重用意味着适合大多数读者,您将看到熟悉的结构 — 尽管是在 XQuery 编程环境中。
回页首系统要求必须安装 Michael Kay 的 Saxon XSLT 和 XQuery 处理程序 SA 感知版 以便执行示例代码。
因为需要 Saxon-SA 版本,必须注册才能获得 30 天的试用期(比如,撰写本文的 。
过程中使用 9.1SA 版进行了测试) 很多示例代码采用了高阶函数以便利用 Saxon-SA 专有的扩展函数(具体而言即 saxon:function)。
将 Saxon 的所有
Java Archive JAR 文件和许可证文件放在 /lib 目录下。
运行文中的例子可通过命令行调用 Saxon,或者使用下载文件中的 Ant 构建文件(我用于测试示例代码)。
如果使用构建文件则应安装最新的 Apache Ant,修改 saxon.lib.dir 属性使其指向包含 Saxon JAR 的 /lib 目录。
要检查 Saxon 是否安装正确,可对 checkSaxon 运行 Ant,若安装正确则该过程将成功完成。
所有 Ant 目标都把结果输出到 /result 目录下。
今天的 XQuery现在我们讨论一下 XQuery 的历史及目前的处境。
XQuery 规范所谓 XQuery 规范,我指的是一组互相关联的文档: XQuery 1.0(一种 XML 查询语言):定义了核心语言 XQuery 1.0 和 XPath 2.0 数据模型: 定义了 XPath 和 XQuery 共同的数据模型 XQuery 1.0 和 XPath 2.0 形式语义:提供了数学基础 XQuery 1.0 和 XPath 2.0 函数与运算符:定义了 XPath 和 XQuery 共有的函数 XSLT 2.0 和 XQuery 1.0 序列化:定义了如何从 XQuery 构造和输出 XML还有人认为 XML Query 用例文档也很有用,它提出了各种应用场景和相应的 XQuery 解决方案 。
(链接参见 参考资料) 还有一些规范和草案提供了其他的功能,比如更新 XML 和全文本搜索。
常用缩写词 API:应用程序编程接口(Application programming interface) EXSLT:XSLT 扩展(Extensions for XSLT) URI:统一资源标识符(Uniform Resource Identifier) W3C:万维网联盟(World Wide Web Consortium) XML:可扩展标记语言(Extensible Markup Language) XSLT:XSL 转换(XSL Transformations)“这么多规范?”有这种想法的人不只您一个, 难以理解为何 W3C Query Working Group 搞了这么多互相关联的文档。
如果说是因为复杂,那么我无法反对,但我确实奇怪为何 XSLT1.0(参见 参考资料)能够用一个规范(或者说两个 — 因为还有一个相关的 XPath 1.0 规范)就能办到。
在 XQuery 1.0 规范的定义过程中,一直有几个问题始终跟着 XQuery。
其中很多提出的极其严重的限制使(当时的)我确信必将阻碍 XQuery 的发展(经过一年紧锣密鼓的开发,大部分最终证明是无关紧要的)。
承认自己的错误是一种好的学习方法,因此我认为可以重新看看这些问题。
永恒的话题 1:XSLT 与 XQuery作为一名活跃的 XSLT 程序员,我非常关心这两种技术的交叉之处。
XSLT 和 XQuery 的语法虽然不同,但在很多方面相同 — 比如,这两种语言: 都输入 XML,输出 XML 具有相同的数据模型和类型系统 都是声明性的,即没有副作用 都使用 XPath,具有相同的内置函数库XQuery 语法这几个例子说明了 XQuery 语法的几个恼人之处: XQuery 经常使用花括号(),而 XSLT 仅在属性值模板(Attribute Value Template,AVT)中使用。
负号(-)需要增加空格。
比如 t-1 是一个合法的变量名,应该写作 t - 1。
XQuery 中的代码注释(: : )让我感到好笑,但我发现再定义一种新方式注释源代码也没有多少必要。
XSLT 和 XQuery 当然也有明显的区别,(从 XSLT 爱好者的立场上)也可以说 XQuery代表 XSLT 2.0 的一个功能子集。
比如,XQuery 没有动态邦定的概念,而 XSLT 通过模板匹配规则提供了动态匹配。
XSLT 还提供了某种形式的多态,即使用 xsl:import 覆盖模板匹配规则。
XQuery 没有这样的设施。
用过 XQuery 之后,我发现有些工作更适合用 XSLT,有的则更适合用 XQuery: 毫无疑问,XQuery 非常适合查询 XML。
我发现,它非常适合提取小段数据,然后 交给 XSLT 格式化和表示。
XQuery 适合控制处理(比如 XSLT),特别是在 XML 数据库中。
需要格式化数字的时候 XSLT 就胜出了。
XQuery 更容易理解,学习曲线也相对平缓。
在 XQuery 中遍历树非常麻烦。
使用 XSLT 模板匹配更合适。
学习 XSLT 和学习 XQueryDan McCreary 写的一篇博客文章(Opinion, , 2008 年 6 月) 讨论了为什么有经验的 SQL开发人员可能会发现 XQuery 比 XSLT 更容易学。
无疑向数据库管理员(DBA)推销 XQuery 要容易得多,十年来他们一直在抱怨 XML 对自己的系统的入侵,抱怨为什么要把 XML 分解并放到他们的关系表中。
注意:关于 XQuery 和 XSLT 更全面的比较,建议您阅读 Benoit Marchal 的文章“Comparing XSLT 2.0 and XQuery”(见 参考资料)。
永恒的话题 2: XML Schema 和数据类型W3C XQuery Working Group 在 XQuery 中引入 XML Schema 数据类型(见 参考资料)引起了极大争议,由于后者未能让整个 XML 社区接受为约束和验证 XML 的最佳方法,这种依赖关系被人们认为颇具讽刺意味。
后来令我吃惊的是,如果不需要就不用去管数据类型,总体而言 XQuery 实现在这一点做得很好。
我不想再重复关于 XML Schema(见 参考资料)或其他模式语言优劣的争论。
可维护性和快速重构等因素可以帮助您决定 XQuery 应用程序中是否使用模式或数据类型。
下面是我遵循的几条非正式的原则: 如果不使用模式,则在函数签名中使用基本数据类型。
如果使用 XML Schema 和数据类型,应考虑自动生成代码(存根代码等)以方便 维护。
通常可以忽略模式和数据类型,等到以后再加上,虽然可能损失一些好处(捕捉错 误)。
第一次使用 XQuery 的时候我建议采用这种办法。
此外,如果不喜欢固定到某种特定的模式技术,在 XQuery 中完全可以不去管它。
永恒的话题 3:强类型与弱类型静态类型还是动态类型是各种程序员社区都常见的永恒话题。
XQuery 只不过是这场旷日持久而有时候无足轻重的争论的另一个(不情愿的)牺牲品罢了。
上一节中曾经提到,XQuery 加入了 XML Schema 数据类型。
在 XQuery 中可以完全忽略它,就是说强弱或者静态动态类型的争论实际上根本没有必要:两种方法都是有效的,用或者不用都是允许的。
也许现在的问题是,“用不用数据类型呢?”数据类型改变了编程的方式,也许应该说明我如何使用数据类型,我为何认为应有限度地使用数据类型。
我发现在函数的输入参数和返回值中使用数据类型就足够了,如 清单 1 所示。
清单 1. local:add 函数xquery version quot1.0quotdeclare function local:adda as xs:integer b as xs:integer as xs:integer xs:integerabdocument ltresultgtlocal:add12lt/resultgt如果运行 exampleSimple Ant 目标文件,结果就会输出到控制台,同时将文件保存到/result 目录下,如 清单 2 所示。
清单 2. 处理 example.xq 的结果Buildfile: src/build.xmlexample:echo Source document ignored - query does not access the context itemecho ltxml versionquot1.0quot encodingquotUTF-8quotgtecho ltresultgt3lt/resultgtBUILD SUCCESSFULTotal time: 1 second除了看到 addition 在 XQuery 中能正常工作以外,我还将 local:add 函数的输入和返回值限定为 XML Schema 数据类型 xs:integer。
如果提供错误的输入 — 比如 local:add1quot2quot(2 被作为一个字符串提供),将会看到有用的类型错误信息。
不考虑数据类型,XQuery 仍然会生成错误,不过这是因为 XQuery 知道加法运算符()应该用于处理数字。
静态类型分析 意味着所有可能的类型错误(主要是指数据类型不匹配)发生在分析而不是执行代码的时候,就是说任何调用 local:add 函数的外部 XQuery 代码都需要在运行前通过静态分析。
通过这种保守的静态检查,数据类型可以改进应用程序的总体质量,因为在部署代码之前必须保证数据类型匹配。
简言之,即使 XML 没有定义的数据类型或者相关的模式,使用基本数据类型也能带来一些好处。
此外,还要考虑应用程序在将来利用 XML Schema 数据类型。
永恒的话题 4:没有更新机制XQuery 的最初设想没有考虑设置更新 XML 文档(在文件系统、数据库等中)的特性。
2008年 3 月,Candidate Recommendation of XQuery Update Facility(XQF — 见 参考资料)纠正了这个弱点。
W3C XQuery Working Group 可能没有时间考虑将这方面的特性加入到 XQuery 核心标准中。
当时没有涉及意味着通过反馈 XQuery 处理程序的实际实现可能会修改 XQueryUpdate Facility 规范。
这种经验在创建规范的时候是很有必要的,我确信 XQF 规范定义的比较好。
回页首XQuery 的局限与不足尽管大多数永恒的话题都消失了,对于当前的 XQuery 语言仍然有一些问题值得讨论。
缺少 system-property 函数XSLT 有一个非常有用的函数, 可以查询处理程序本身的信息,返回处理程序供应商或者当前 XSLT 版本之类的属性。
如果需要创建跨 XQuery 处理程序执行的 XQuery 应用程序,这种自省是很有用的。
我只能说 XQuery 没有,或者 — 可能更准确 — XPath 2.0 没有任何机制执行该函数或者可供 XQuery 使用的方法。
警告:使用扩展函数可能造成锁定多数 XQuery 实现都增加了自己的第三方函数,提供了各种各样的附加功能。
使用这些扩展函数最明显的问题是,如果依赖于特定实现提供的专用非标准功能,有可能造成 XQuery代码不兼容。
得到 EXSLT 的支持前我一直与这种供应商锁定问题作斗争。
XQuery 也出现这种情况我毫不奇怪。
要说明扩展函数对开发的副作用,最好的办法 是举一个简单的例子。
我调查了三种 XQuery实现(见 参考资料)中的 random 函数的实现,因为难以通过小的例子说明情况可能有多么复杂: eXist XML 数据库: o util:random xs:double o util:randoma as xs:integer xs:integer o math:random xs:double MarkLogic XML 数据库: o xdmp:randommax as xs:unsignedLong 作 为 xs:unsignedLong Saxon XQuery 和 XSLT 处理程序: o math:random: 返回 0 和 1 之间的随机数 o random:random-sequence1seed: 根据初始种子返回一组 随机数六种不同的 random 函数,如果扩展函数库中生成随机数都有这么多变化,可以想象编写兼容的 XQuery 代码有多么困难。
eXist XML 数据库的三种函数可能显示了最糟糕的情况,因为 util:random 和 math:random 之间的关系不明确。
我认为这是由于对不同作者编写的两个函数重构(常见于开放源代码)造成的。
最糟糕的是,这两个函数可能代表新旧不同的版本。
此外,eXist 的 util:random 函数增加了更多的变化,它接受 xs:integer 种子参数并返回一个 xs:integer(而不是其他两个函数返回的 xs:double)。
MarkLogic 定义了一个 randommax 函数返回一个随机的、 介于零和最长 64 位数之间的无符号整数。
返回类型 xs:unsignedLong 代表 0 和 709551615(含)之间的无符号整数。
该函数允许提供同类型的 max 参数来定义最大值。
Saxon 选择支持 EXSLT,应该是一件好事(有关 EXSLT 请参见 参考资料)。
但是由于目前 EXSLT 和 XQuery 没有联系,使用 Saxon random 函数有点混乱。
对于 random 函数的 XQuery 处理程序实现的混乱情况,Saxon random 函数可能是最好的一个例子。
多种不同的名称空间再加上不同的函数签名,要想创建跨平台的运行时 XQuery 应用程序基本上是不可能的,因为多数 XQuery 处理程序在静态分析阶段就会被阻塞。
默认名称空间的麻烦XQuery 支持定义默认名称空间,所有不带前缀的输出元素都出现在默认名称空间中。
利用这一特性,可以定义 XQuery XML 输出中所有无前缀元素的默认名称空间,如 清单 3 所示。
清单 3. 声明默认元素名称空间xquery version quot1.0quotdeclare default element namespace quothttp://www.w3.org/1999/xhtmlquotlthtmlgt ltbodygt test lt/bodygtlt/htmlgt运行这段代码将得到结构良好的输出结果(如 清单 4 所示),所有元素都存在于http://www.w3.org/1999/xhtml 名称空间中。
当然也可选择将名称空间绑定到前缀,虽然这样可能会麻烦一些,因为使用文档类型定义(DTD)的时候要求元素没有前缀。
清单 4. no-namespace.xq 的输出ltxml versionquot1.0quot encodingquotUTF-8quotgtlthtml xmlnsquothttp://www.w3.org/1999/xhtmlquotgtltbodygttestlt/bodygtlt/htmlgt因此如果声明默认名称空间,就会带来几个问题。
首先,没有名称空间怎么能产生不带前缀的结果元素呢?奇怪的是,XQuery 允许把前缀绑定到一个空字符串,表示将元素放到默认名称空间中。
如 清单 5 所示。
清单 5. no 名称空间绑定declare namespace my-no-namespace quotquot这样,my-no-namespace:myelement 就有一个 no 名称空间。
但是如果在 no 名称空间中需要不带前缀但仍使用定义的默认名称空间的元素,麻烦就来了。
在 XQuery 中这样做是不可能的,就是说必须在 XQuery 处理之后再借助于文本操作来删除前缀和这些人为的名称空间声明。
所幸的是,这些古怪的概念在各种 XQuery 处理程序中都是一致的。
能否保留要看其用例是否足够强大到可以保证其应用。
没有 XQuery 求值或者动态 XPath在 XSLT 中不通过第三方函数就无法动态生成和计算 XPath。
这种局限同样适用于XQuery,就是不能对动态生成的 XPath 求值。
此外,也不能动态计算生成的 XQuery。
XQuery 没有 eval 类型的函数执行生成的 XQuery 代码,使得一些惯用法难以实现。
所幸的是,多数成熟的 XQuery 处理程序都增加了自己的 eval 函数。
在具有良好架构的代码中,不要使用该函数,尽管用起来通常很方便。
现在您可能有点糊涂了。
我要说的是,在任何计算环境中动态求值代码都需要知道什么时候使用它,什么时候不使用它。
异常处理机制XQuery 允许使用 fn:error 函数随机抛出错误,但是没有提供检测静态或动态错误以及处理错误的设施。
我想 fn:error 方法基本相当于 throw 。
一些 XQuery 处理程序提供了 try/catch 之类的扩展函数,但一般来说最好避免使用这些扩展函数。
太多的可选特性XQuery 实现要符合规范,必须提供一组核心特性。
除了核心特性集之外,实现者还可以决定是否提供某些可选特性,如下所列: Schema import:模式直接通过序言导入 Schema 验证:用模式技术验证 XML 静态类型分析 :在分析阶段检查各种静态类型错误 模块:支持库模块和模块导入 序列化:将查询结果序列化为 XML 文档 轴:支持 ancestor、ancestor-or-self、following、following-sibling、preceding 和 preceding-sibling 轴将模式导入和处理作为可选特性是明智的决策,避免很多潜在的冲突。
还有一个次要的好处是避免了 XQuery 核心出现依赖关系。
静态分析作为可选特性也是可取的。
XQuery 中的模块是创建可重用代码库的基础,很难让人理解为何是可选的。
另外一个同样困难的选择是为何开发人员不希望控制 XQuery 的 XML 输出,有一个先例,XSLT 将序列化定义为标准(参见 参考资料)。
序列化存在不一致性 — 比方说,查询结果通常是一系列的项,而不是一个 XML 文档。
但多数处理程序都默认输出 XML。
比如 清单 6,被称为 quine。
在编程中,quine 指的是执行时输出自身代码清单的一段代码。
Quine 对于说明任何编程语言的局限都是一个有用的测试,XQuery 的测试如清单 6 所示。
清单 6. XQuery 的 quine 测试xquery version quot1.0quotdeclare variable s:xquery version quot1.0quotdeclare variable s:quotquotfn:substrings144sfn:substrings45fn:substrings 144sfn:substrings45执行 exampleQuine Ant 目标程序将得到 清单 7 所示的结果。
清单 7. 用 XQuery 处理 quine 的结果ltxml versionquot1.0quot encodingquotUTF-8quotgtxquery version quot1.0quotdeclare variable s:quot xquery version quot1.0quotdeclare variable s:quotquotfn:substrings144sfn:substrings45quotfn:substrings144sfn:substrings45由于空白的原因,这段代码惟一的问题是需要使用一个处理程序专有的选项来控制版本/编码的输出 — 不算太完美。
它说明了 XQuery 核心的序列化部分。
更重要的是多数 XQuery处理默认使用 XML 输出序列化,尽管上述查询的结果是一系列字符串!再看看上述最后一个可选特性,XQuery 可选的支持某些 XPath 轴差不多是令人反感的。
最初,人们认为这些轴实现起来很难,但后来证明这些可选的轴可以用其他支持的轴来表示。
多数 XQuery 处理程序都实现了这些可选的轴,因此不需要再讨论这个问题了。
将其交给实现者吧我刚刚讨论了您可能决定实现的很多特性。
此外,还有一些可选特性没有定义,因此必须根据需要进一步定义其功能。
允许实现者决定如何 实现某个特性会带来很多变化。
下面列出了一些这样的特性: 支持 XML 1.0 和 Namespaces 1.0 或者 XML 1.1 和 Namespaces 1.1 实现扩展函数 预声明名称空间,包括默认元素和函数名称空间 内置模式,可在查询中使用其类型名,在验证中使用元素和属性声明 内置全局变量以及和默认值有关的选项 指定支持的排列法 静态文本 Base-URI 的默认值、边界空间策略、空序规定、名称空间复制方式、构 造方式、默认排列法和顺序方式 序列化选项的默认值 外部变量定义您可能知道列出的大多数可选特性。
但是,如果让实现者自由选择如何编写软件以保证兼容性是很难的。
我认为对于 XQuery,我们可能确定的可选特性太多,最终只会带来不兼容性。
对 XQuery 的优劣评价就到此为止。
现在您已经了解 XQuery 开发中的不足,我们来介绍一些编程惯用法和例子。
使用扩展函数在您自己的代码寻找一种使用扩展函数的方法。
如果只有 system-property 函数如果有 system-property 函数,实现组成 XQuery 函数库就相对容易些,后者包括 XQuery 处理程序专用的函数。
清单 8 中的代码说明了如何包装不同的实现,如何在各种供应商 random 函数实现中切换(参见 上一节)。
清单 8. util:random 伪代码declare function util:ra.