XQuery 高级应用 开发应用程序惯用法文档格式.docx
《XQuery 高级应用 开发应用程序惯用法文档格式.docx》由会员分享,可在线阅读,更多相关《XQuery 高级应用 开发应用程序惯用法文档格式.docx(37页珍藏版)》请在冰点文库上搜索。
![XQuery 高级应用 开发应用程序惯用法文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-4/28/57d4bd35-0f6b-49c3-82d0-67de39b8ceb8/57d4bd35-0f6b-49c3-82d0-67de39b8ceb81.gif)
本教程是为那些对XML技术有一定了解而对XSLT或Query略有涉猎的开发人员编写的。
这里提出的编程惯用法并不是我发明的,在多种计算机语言中都以各种形式存在。
这种重用意味着适合大多数读者,您将看到熟悉的结构—尽管是在XQuery编程环境中。
系统要求
必须安装MichaelKay的
SaxonXSLT和XQuery处理程序SA感知版
以便执行示例代码。
因为需要Saxon-SA版本,必须注册才能获得30天的试用期(比如,撰写本文的过程中使用9.1SA版进行了测试)。
很多示例代码采用了高阶函数以便利用Saxon-SA专有的扩展函数(具体而言即
saxon:
function())。
将Saxon的所有Java™Archive(JAR)文件和许可证文件放在/lib目录下。
运行文中的例子可通过命令行调用Saxon,或者使用下载文件中的Ant构建文件(我用于测试示例代码)。
如果使用构建文件则应安装最新的ApacheAnt,修改
saxon.lib.dir
属性使其指向包含SaxonJAR的/lib目录。
要检查Saxon是否安装正确,可对checkSaxon
运行Ant,若安装正确则该过程将成功完成。
所有Ant目标都把结果输出到/result目录下。
今天的XQuery
现在我们讨论一下XQuery的历史及目前的处境。
XQuery规范
所谓
XQuery规范,我指的是一组互相关联的文档:
∙XQuery1.0(一种XML查询语言):
定义了核心语言
∙XQuery1.0和XPath2.0数据模型:
定义了XPath和XQuery共同的数据模型
∙XQuery1.0和XPath2.0形式语义:
提供了数学基础
∙XQuery1.0和XPath2.0函数与运算符:
定义了XPath和XQuery共有的函数
∙XSLT2.0和XQuery1.0序列化:
定义了如何从XQuery构造和输出XML
还有人认为XMLQuery用例文档也很有用,它提出了各种应用场景和相应的XQuery解决方案(链接参见
参考资料)。
还有一些规范和草案提供了其他的功能,比如更新XML和全文本搜索。
常用缩写词
∙API:
应用程序编程接口(Applicationprogramminginterface)
∙EXSLT:
XSLT扩展(ExtensionsforXSLT)
∙URI:
统一资源标识符(UniformResourceIdentifier)
∙W3C:
万维网联盟(WorldWideWebConsortium)
∙XML:
可扩展标记语言(ExtensibleMarkupLanguage)
∙XSLT:
XSL转换(XSLTransformations)
“这么多规范?
”有这种想法的人不只您一个,难以理解为何W3CQueryWorkingGroup搞了这么多互相关联的文档。
如果说是因为复杂,那么我无法反对,但我确实奇怪为何XSLT1.0(参见
参考资料)能够用一个规范(或者说两个—因为还有一个相关的XPath1.0规范)就能办到。
在XQuery1.0规范的定义过程中,一直有几个问题始终跟着XQuery。
其中很多提出的极其严重的限制使(当时的)我确信必将阻碍XQuery的发展(经过一年紧锣密鼓的开发,大部分最终证明是无关紧要的)。
承认自己的错误是一种好的学习方法,因此我认为可以重新看看这些问题。
永恒的话题1:
XSLT与XQuery
作为一名活跃的XSLT程序员,我非常关心这两种技术的交叉之处。
XSLT和XQuery的语法虽然不同,但在很多方面相同—比如,这两种语言:
∙都输入XML,输出XML
∙具有相同的数据模型和类型系统
∙都是声明性的,即没有副作用
∙都使用XPath,具有相同的内置函数库
XQuery语法
这几个例子说明了XQuery语法的几个恼人之处:
∙XQuery经常使用花括号({}),而XSLT仅在属性值模板(AttributeValueTemplate,AVT)中使用。
∙负号(-)需要增加空格。
比如
$t-1
是一个合法的变量名,应该写作
$t-1。
∙XQuery中的代码注释((:
:
))让我感到好笑,但我发现再定义一种新方式注释源代码也没有多少必要。
XSLT和XQuery当然也有明显的区别,(从XSLT爱好者的立场上)也可以说XQuery代表XSLT2.0的一个功能子集。
比如,XQuery没有动态邦定的概念,而XSLT通过模板匹配规则提供了动态匹配。
XSLT还提供了某种形式的多态,即使用
xsl:
import
覆盖模板匹配规则。
XQuery没有这样的设施。
用过XQuery之后,我发现有些工作更适合用XSLT,有的则更适合用XQuery:
∙毫无疑问,XQuery非常适合查询XML。
我发现,它非常适合提取小段数据,然后交给XSLT格式化和表示。
∙XQuery适合控制处理(比如XSLT),特别是在XML数据库中。
∙需要格式化数字的时候XSLT就胜出了。
∙XQuery更容易理解,学习曲线也相对平缓。
∙在XQuery中遍历树非常麻烦。
使用XSLT模板匹配更合适。
学习XSLT和学习XQuery
DanMcCreary写的一篇博客文章(Opinion,2008年6月),讨论了为什么有经验的SQL开发人员可能会发现XQuery比XSLT更容易学。
无疑向数据库管理员(DBA)推销XQuery
要容易得多,十年来他们一直在抱怨XML对自己的系统的入侵,抱怨为什么要把XML分解并放到他们的关系表中。
注意:
关于XQuery和XSLT更全面的比较,建议您阅读BenoitMarchal的文章“ComparingXSLT2.0andXQuery”(见
永恒的话题2:
XMLSchema和数据类型
W3CXQueryWorkingGroup在XQuery中引入XMLSchema数据类型(见
参考资料)引起了极大争议,由于后者未能让整个XML社区接受为约束和验证XML的最佳方法,这种依赖关系被人们认为颇具讽刺意味。
后来令我吃惊的是,如果不需要就不用去管数据类型,总体而言XQuery实现在这一点做得很好。
我不想再重复关于XMLSchema(见
参考资料)或其他模式语言优劣的争论。
可维护性和快速重构等因素可以帮助您决定XQuery应用程序中是否使用模式或数据类型。
下面是我遵循的几条非正式的原则:
∙如果不使用模式,则在函数签名中使用基本数据类型。
∙如果使用XMLSchema和数据类型,应考虑自动生成代码(存根代码等)以方便维护。
∙通常可以忽略模式和数据类型,等到以后再加上,虽然可能损失一些好处(捕捉错误)。
第一次使用XQuery的时候我建议采用这种办法。
此外,如果不喜欢固定到某种特定的模式技术,在XQuery中完全可以不去管它。
永恒的话题3:
强类型与弱类型
静态类型还是动态类型是各种程序员社区都常见的永恒话题。
XQuery只不过是这场旷日持久而有时候无足轻重的争论的另一个(不情愿的)牺牲品罢了。
上一节中曾经提到,XQuery加入了XMLSchema数据类型。
在XQuery中可以完全忽略它,就是说强弱或者静态动态类型的争论实际上根本没有必要:
两种方法都是有效的,用或者不用都是允许的。
也许现在的问题是,“用不用数据类型呢?
”数据类型改变了编程的方式,也许应该说明我如何使用数据类型,我为何认为应有限度地使用数据类型。
我发现在函数的输入参数和返回值中使用数据类型就足够了,如
清单1
所示。
清单1.local:
add函数
xqueryversion"
1.0"
;
declarefunctionlocal:
add($aasxs:
integer,$basxs:
integer)asxs:
integer{
xs:
integer($a+$b)
};
document{
<
result>
{
local:
add(1,2)
}
/result>
}
如果运行
exampleSimple
Ant目标文件,结果就会输出到控制台,同时将文件保存到/result目录下,如
清单2
清单2.处理example.xq的结果
Buildfile:
src/build.xml
example:
[echo]Sourcedocumentignored-querydoesnotaccessthecontextitem
[echo]<
?
xmlversion="
encoding="
UTF-8"
>
3<
BUILDSUCCESSFUL
Totaltime:
1second
除了看到addition在XQuery中能正常工作以外,我还将
local:
add
函数的输入和返回值限定为XMLSchema数据类型xs:
integer。
如果提供错误的输入—比如
add(1,"
2"
)(2被作为一个字符串提供),将会看到有用的类型错误信息。
不考虑数据类型,XQuery仍然会生成错误,不过这是因为XQuery知道加法运算符(+)应该用于处理数字。
静态类型分析
意味着所有可能的类型错误(主要是指数据类型不匹配)发生在分析而不是执行代码的时候,就是说任何调用local:
函数的外部XQuery代码都需要在运行前通过静态分析。
通过这种保守的静态检查,数据类型可以改进应用程序的总体质量,因为在部署代码之前必须保证数据类型匹配。
简言之,即使XML没有定义的数据类型或者相关的模式,使用基本数据类型也能带来一些好处。
此外,还要考虑应用程序在将来利用XMLSchema数据类型。
永恒的话题4:
没有更新机制
XQuery的最初设想没有考虑设置更新XML文档(在文件系统、数据库等中)的特性。
2008年3月,CandidateRecommendationofXQueryUpdateFacility(XQF—见
参考资料)纠正了这个弱点。
W3CXQueryWorkingGroup可能没有时间考虑将这方面的特性加入到XQuery核心标准中。
当时没有涉及意味着通过反馈XQuery处理程序的实际实现可能会修改XQueryUpdateFacility规范。
这种经验在创建规范的时候是很有必要的,我确信XQF规范定义的比较好。
XQuery的局限与不足
尽管大多数永恒的话题都消失了,对于当前的XQuery语言仍然有一些问题值得讨论。
缺少system-property()函数
XSLT有一个非常有用的函数,可以查询处理程序本身的信息,返回处理程序供应商或者当前XSLT版本之类的属性。
如果需要创建跨XQuery处理程序执行的XQuery应用程序,这种自省是很有用的。
我只能说XQuery没有,或者—可能更准确—XPath2.0没有任何机制执行该函数或者可供XQuery使用的方法。
警告:
使用扩展函数可能造成锁定
多数XQuery实现都增加了自己的第三方函数,提供了各种各样的附加功能。
使用这些扩展函数最明显的问题是,如果依赖于特定实现提供的专用非标准功能,有可能造成XQuery代码不兼容。
得到
EXSLT
的支持前我一直与这种供应商锁定问题作斗争。
XQuery也出现这种情况我毫不奇怪。
要说明扩展函数对开发的副作用,最好的办法
是举一个简单的例子。
我调查了三种XQuery实现(见
参考资料)中的
random()
函数的实现,因为难以通过小的例子说明情况可能有多么复杂:
∙eXistXML数据库:
outil:
random()xs:
double
random($aasxs:
integer)xs:
integer
omath:
∙MarkLogicXML数据库:
oxdmp:
random([$maxasxs:
unsignedLong])
作为
xs:
unsignedLong
∙SaxonXQuery和XSLT处理程序:
random():
返回0和1之间的随机数
orandom:
random-sequence(1,$seed):
根据初始种子返回一组随机数
六种不同的
函数,如果扩展函数库中生成随机数都有这么多变化,可以想象编写兼容的XQuery代码有多么困难。
eXistXML数据库的三种函数可能显示了最糟糕的情况,因为
util:
random
和
math:
之间的关系不明确。
我认为这是由于对不同作者编写的两个函数重构(常见于开放源代码)造成的。
最糟糕的是,这两个函数可能代表新旧不同的版本。
此外,eXist的
函数增加了更多的变化,它接受
integer
种子参数并返回一个
integer(而不是其他两个函数返回的
double)。
MarkLogic定义了一个
random($max)
函数返回一个随机的、介于零和最长64位数之间的无符号整数。
返回类型
unsignedLong
代表0和184********709551615(含)之间的无符号整数。
该函数允许提供同类型的
$max
参数来定义最大值。
Saxon选择支持EXSLT,应该是一件好事(有关EXSLT请参见
但是由于目前EXSLT和XQuery没有联系,使用Saxon
函数有点混乱。
对于
函数的XQuery处理程序实现的混乱情况,Saxon
函数可能是最好的一个例子。
多种不同的名称空间再加上不同的函数签名,要想创建跨平台的运行时XQuery应用程序基本上是不可能的,因为多数XQuery处理程序在静态分析阶段就会被阻塞。
默认名称空间的麻烦
XQuery支持定义默认名称空间,所有不带前缀的输出元素都出现在默认名称空间中。
利用这一特性,可以定义XQueryXML输出中所有无前缀元素的默认名称空间,如
清单3
清单3.声明默认元素名称空间
declaredefaultelementnamespace"
http:
//www.w3.org/1999/xhtml"
html>
<
body>
test
/body>
/html>
运行这段代码将得到结构良好的输出结果(如
清单4
所示),所有元素都存在于http:
//www.w3.org/1999/xhtml名称空间中。
当然也可选择将名称空间绑定到前缀,虽然这样可能会麻烦一些,因为使用文档类型定义(DTD)的时候要求元素没有前缀。
清单4.no-namespace.xq的输出
htmlxmlns="
test<
因此如果声明默认名称空间,就会带来几个问题。
首先,没有名称空间怎么能产生不带前缀的结果元素呢?
奇怪的是,XQuery允许把前缀绑定到一个空字符串,表示将元素放到默认名称空间中。
如
清单5
清单5.no名称空间绑定
declarenamespacemy-no-namespace="
"
这样,my-no-namespace:
myelement
就有一个no名称空间。
但是如果在no名称空间中需要不带前缀但仍使用定义的默认名称空间的元素,麻烦就来了。
在XQuery中这样做是不可能的,就是说必须在XQuery处理之后再借助于文本操作来删除前缀和这些人为的名称空间声明。
所幸的是,这些古怪的概念在各种XQuery处理程序中都是一致的。
能否保留要看其用例是否足够强大到可以保证其应用。
没有XQuery求值或者动态XPath
在XSLT中不通过第三方函数就无法动态生成和计算XPath。
这种局限同样适用于XQuery,就是不能对动态生成的XPath求值。
此外,也不能动态计算生成的XQuery。
XQuery没有
eval()
类型的函数执行生成的XQuery代码,使得一些惯用法难以实现。
所幸的是,多数成熟的XQuery处理程序都增加了自己的
函数。
在具有良好架构的代码中,不要使用该函数,尽管用起来通常很方便。
现在您可能有点糊涂了。
我要说的是,在任何计算环境中动态求值代码都需要知道什么时候使用它,什么时候不使用它。
异常处理机制
XQuery允许使用
fn:
error()
函数随机抛出错误,但是没有提供检测静态或动态错误以及处理错误的设施。
我想
方法基本相当于
throw。
一些XQuery处理程序提供了
try/catch
之类的扩展函数,但一般来说最好避免使用这些扩展函数。
太多的可选特性
XQuery实现要符合规范,必须提供一组核心特性。
除了核心特性集之外,实现者还可以决定是否提供某些可选特性,如下所列:
∙Schemaimport:
模式直接通过序言导入
∙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测试
declarevariable$s:
='
="
substring($s,1,44),$s,fn:
substring($s,45)'
substring($s,1,44),
$s,
substring($s,45)
执行
exampleQuine
Ant目标程序将得到
清单7
所示的结果。
清单7.用XQuery处理quine的结果
xqueryversion"
由于空白的原因,这段代码惟一的问题是需要使用一个处理程序专有的选项来控制版本/编码的输出—不算太完美。
它说明了XQuery核心的序列化部分。
更重要的是多数XQuery处理默认使用XML输出序列化,尽管上述查询的结果是一系列字符串!
再看看上述最后一个可选特性,XQuery可选的支持某些
XPath轴差不多是令人反感的。
最初,人们认为这些轴实现起来很难,但后来证明这些可选的轴可以用其他支持的轴来表示。
多数XQuery处理程序都实现了这些可选的轴,因此不需要再讨论这个问题了。
将其交给实现者吧
我刚刚讨论了您可能决定实现的很多特性。
此外,还有一些可选特性没有定义,因此必须根据需要进一步定义其功能。
允许实现者决定如何
实现某个特性会带来很多变化。
下面列出了一些这样的特性:
∙支持XML1.0和Namespa