- 浏览: 70409 次
- 性别:
- 来自: 北京
-
最新评论
在Java环境中使用 XQuery
来源:ibm 发布时间:2008-07-24 收藏 投稿 字体:【大 中 小】
XML 数据格式很难搜索,但是最近随着 XQuery API 的出现,XML 搜索变得非常灵活和简单。对于使用 SAX、DOM、JDOM、JAXP 等处理 XML 文档的 Java™ 程序员来说,工具箱中增加 XQuery API for Java 是一件值得高兴的事。现在 Java 程序员不用再求助于系统调用或者笨拙的 API 就能利用 XQuery 的强大功能了,Sun 提供了一套完整的、标准化的包。
SQL 数据库、XML 数据和查询
虽然编程领域 — 特别是 Java 编程 — 不断发展,可供选择的标准的数量也在增长。换句话说,越来越多的 API 得到 Sun 的认可或者批准。标准化的结果是越来越多的开发者背弃了自己最具竞争力的技术,而去学习新技术。
要掌握的最有吸引力和价值的是那些和数据处理有关的工具和 API。无论应用程序多么酷或者智能,说到底只有能够处理数据才有用。同时,虽然 API 数量的不断增长,但流行和通用的数据格式数量却不断减少。虽然有些数据管理员仍然使用面向对象的数据库管理系统或者 XML 驱动的数据库,但关系数据库(RDBMS)已成为主流,而且仍然是大部分数据管理员的选择。因此 Java 开发人员必须通过 JDBC(数据库连接)或者 JDO (Java Data Objects) 与 SQL 数据库交互。
SQL 数据库与其他数据库
虽然本文提到了查询和数据库,但所指的是 SQL 数据库 — 也称为关系数据库。不过 XML 数据库甚至对象数据库都有一些很好的应用程序。
如果对 XML 数据库感兴趣,可以查看 DB2® Express-C,可免费下载(链接参见 参考资料)。最明显的一点是,XML 数据库在 XML 文档和关系数据之间进行不必要的转换。本文还认为,XQuery 实际上已变为数据库查询语言,因为 XML 数据库以 XML 格式存储数据。
数据库之外的数据基本上也都以 XML 作为标准数据格式。XML 虽然冗长但是健壮,Java 语言中处理 XML 的 API 可能比其他语言都多。无论是解析、数据绑定还是转换,如果应用程序不能处理 XML,就会被认为有局限性甚至有点落后。
两者看似无关,— 数据往往保存在 SQL 数据库中,数据库之外的数据越来越多地采用 XML 格式 —,但是却造成了一些独特的问题。SQL 数据库容易查询,而 XML 文档则不是这样。消费者希望能够方便地搜索数据,数据库中的数据查询方便,但是 XML 文档中的数据就不那么容易了。显然,将 XML 格式的数据塞到数据库中以方便搜索的做法是行不通的。于是就出现了 XQuery — 相应的也就有了 XQuery API for Java (XQJ)。
XQuery:三种技术
经常用到的缩写词
API:应用程序编程接口
DOM:文档对象模型
GUI:图形用户接口
IDE:集成开发环境
JAXP:Java API for XML Processing
SAX:Simple API for XML
W3C:万维网联盟
XML:可扩展标记语言
简言之,XQuery 就是用于定义 XML 文档搜索的一种语言。就像 SQL 赋予了 SELECT 和 FROM 特定的含义一样 — 在一定的上下文中 — XQuery 定义了正斜杠(/)和地址符号(@)以及其他一些关键字和关键字符的意义。
XQuery 的核心包括三部分:
XPath 规范:在 XML 文档中选择零个、一个或多个节点的方法。
选择特定 XML 文档、为 XPath 返回的节点增加选择条件的附加语法
API — 比如 XQJ,XQuery for Java API— 用具体的编程语言对 XQuery 表达式求值
要真正精通 XQuery,必须切实掌握好这三个方面。对于 Java 程序员来说,显然就意味着要学习 XPath、学习新增的 XPath 语法结构,然后将其与基于 Java 的 API 结合起来对 XML 文档使用 XQuery 表达式。
好消息是 XPath 和 XQuery 语法都非常简单明了。如果曾经使用 UNIX® shell、Mac OS X 终端或者 DOS 窗口研究过目录结构,就具备了必要的基础。掌握了小于(<)、大于(>)和等于(=)这些运算符的基本用法外,您称得上是大半个 XPath 专家了。
XPath 基础
事实上,XQuery 差不多完全依赖于另一个 XML 规范,即 XPath 规范。XPath 的作用基本上就是定义创建指向 XML 文档某一部分的路径 的方法。比如,XPath /play/act/scene 就是指 play 根元素下的 act 元素下的所有 scene 元素。
XPath 是相对路径
最基本的 XPath 使用元素名和正斜杠。默认情况下,XPath 以 XML 文档中的当前位置开始。因此如果使用 DOM,比如导航到 speech 元素,然后指定路径 speaker,则指向当前位置的 speech 元素中的所有 speaker 元素。因此 XPath 的求值是相对于文档中的位置而言的。
从当前节点移动
要移动到文档的根元素,可在路径前加上正斜杠。无论在文档的什么位置,/play 可以查找名为 play 的根元素。用 ../ 可以选择当前元素的父元素。应该能够看出这实际上非常类似与目录结构。路径 ../../personae/title 将从当前元素上溯两层,然后依次寻找 personae 和嵌套在其中的 title 元素。
选择属性
除了元素外还有很多可供选择的其他内容。比如这个 XPath:/cds/cd@title。它返回根元素 cds 下 cd 元素上所有的 “title” 属性。
要记住,@ 返回的不是属性的值而是属性本身。因此,@isbn 将选择所有名为 isbn 属性而不是这些属性的值。此外,XPath 中的属性指的是属性名和属性值(请参阅后面的 关于节点 一节)。
选择文本
文本嵌套在元素中
对于元素和元素中的文本,通常的想法是认为元素 “具有文本”。经常看到这样的说法,“title 元素的值是 ‘You Can't Count On Me.’”。这是不正确的,实际上文本嵌套在元素中。可以认为文本属于一个元素,但是和属性具有文本值完全不同。
最好的办法是认为文本确实嵌套在元素中。元素是文本的双亲,文本不是它所嵌套的元素的值。
就像选择元素和属性一样,也可选择元素中的文本。如果 XPath 以元素名结束,比如 /cds/cd/title,则选择的是元素 — 而且不 包括这些元素中的文本。如果需要元素中的文本,可使用 text() 语法。因此要取得所有 CD 的文字标题,可以使用 /cds/cd/title/text() 这样的路径。该路径不提供任何元素,而是指定元素中的文本。
XPath 选择一组节点
有效使用 XPath 的一个关键是要认识到 XPath 始终计算的是节点集。这个集合可能包含零个、一个或多个节点,但 XPath 的结果必定 是集合。多数人在编写 XPath 的时候认为:该路径返回一个元素、一个属性或者文本,但事实并非如此。不过这也不是绝对的。
如果使用 DOM,应该已经对节点有所了解了。对于 DOM 来说,XML 文档中的一切 — 元素、属性和文本 — 都是节点。元素有元素节点,属性(包括属性值)有属性节点,元素中的文本则是文本节点。因此路径 ../../personae/title 最终将选择 title 元素,实际上返回一个节点集。这个集合可能包含零个(没有匹配的元素)、一个或者多个节点。集合中的所有节点都是名为 “title” 的元素。
随着路径变得更加复杂,有可能选择一个更大的集合 — 也许同时包括属性和元素,或者同时包括文本和元素 — 无论如何这些路径最终都是选择一个节点集。牢记这一点是正确使用 XQuery 的关键。通过 XPath 选择一个节点集,而使用 XQuery 则通常是按照一定条件选择这些节点的一个子集,也有可能连接多个节点集然后应用搜索条件。只要记住集合中的元素可能有多种类型(元素、文本或者属性),就能更好地编写路径并保证得到预期的结果。
增加 XPath 的选择性
前面我们看到了如何根据节点名(元素和属性)以及父节点(文本或者选择给定节点的所有子节点)来选择节点集。这本身已经非常强大了,不过 XPath 还提供了更多的选择性,即使用所谓的谓词。
谓词语法
Microsoft® Internet Explorer®(及其他一些浏览器)的错误
XPath 规范要求,谓词 [1] 指向集合的第一个节点,换句话说,XPath 节点集的索引是从 1 开始的。但是,Internet Explorer 多数版本的 XPath 实现都错误地采用零基数索引,[1] 指向数组中的第二 项,[0] 表示第一项。您需要在浏览器中测试,但集合中第一项的索引为 [1] 而不是 [0],这才是正确的。
谓词是用于已有节点集的部分表达式。谓词放在方括号 [ 和 ] 中。作用于谓词左侧的路径所定义的节点集。比如路径 /cds/cd 选择了根元素 “cds” 中的所有 cd 元素。假设需要第一张唱片,可以使用谓词来实现:/cds/cd[1]。它返回路径 /cds/cd 选择的第一个节点。
谓词可用于任何节点集
要记住,谓词作用于谓词本身左侧的节点集。但是这并不意味着谓词只能出现在 XPath 的最后面。可以将 XPath 本身看作是路径的集合,每个路径都返回一个节点集,路径后面的部分作用于该集合并进一步细化。因此 /cds/cd/title 实际上是三个路径:
/cds 返回根元素 “cds”(只含一个元素节点的集合)
cd(相对于上一个节点集)返回上一节点集中嵌套的所有 cd 元素
title(同样相对于上一节点集)返回上一节点集中嵌套的所有 title 元素
谓词必须作用于节点集,但是可作用于任何 节点集。/cds[1]/cd[2]/title[1] 这样的路径是完全合法的。它选择了 /cds 所选节点集中的第一项,/cds[1]/cd 所选节点的第二项,/cds[1]/cd[2]/title 所选节点的第一项。
注意:该路径中有些部分毫无意义,比如使用 / 选择根元素然后再使用 [1] 谓词总是返回集合中的第一个(惟一的一个)元素。只有当节点集本身为空,即给出的根元素名称和文档实际的根元素不同,这种情况下才不会返回任何元素。但是作为一个例子 — 包括从技术的角度看 — XPath 本身以及使用的谓词都没有问题。
超越谓词中的数值索引
当然,只能用数值引用位置的 API 作用是很有限的。这样的模型中,您必须知道需要的项到底在什么位置。不过 XPath 提供了更多的选择。首先,在谓词中可以使用 last() 函数选择集合中的最后一项,不论集合中包含多少项。/cds/cd[last] 选择文档中最后一个 cd。
使用 position() 函数还可以选择在特定位置之前或者之后的所有项。position() 函数返回给定节点在集合中的位置。比如,假设需要前 5 张唱片,可以使用路径 /cds/cd[position()<6]。这样就能选择 position() 结果小于 6 的所有节点。
根据数据选择节点
最后 — 对于这里关于 XPath 的简要介绍而不是详细讨论来说 — 还可以根据节点的子元素或者节点的属性来选择节点。就像 XPath 后面的部分依赖于前面路径所决定的节点集一样,集合的谓词也依赖于所应用的集合。谓词除了能够使用小于(<)和大于(>)这样的运算符外,还可根据选中节点的数据而不仅仅是这些节点在整个集合中的位置进行选择。
比方说要选取属性 “style” 为 “folk” 的所有 CD。表达式应该首先选择全部 CD,然后把这些 CD 的 style 属性和 “folk” 进行比较。其 XPath 形式为 /cds/cd[@style='folk']。按照前面的说明,这个表达式的意思应该很清楚了。首先,通过 /cds/cd 选择了一个节点集。然后使用谓词 @style 取出每个节点的 “style” 属性。前面已经提到,@ 表示属性。并且该属性是关于已经选择的节点集的(即所有 cd 元素)。接下来把这些属性的值和字符串 “folk” 比较。属性匹配的返回,其他的则在结果集中排除掉。
该方法也可用于所选集合的嵌套元素。假设文档结构如清单 1 所示。
清单 1. CD 清单文档的结构
<cds>
<cd style="some-style">
<title>CD title</title>
<track-listing>
<track>Track title</track>
<!-- More track elements... -->
</track-listing>
</cd>
<!-- More CDs... -->
</cds>
比方说如果希望取得包含 10 条或更多音轨的 CD。首先要选择全部 CD 元素,即前面多次用到的路径:/cds/cd。然后使用谓词得到关于所选集合的特定节点集的项数,即嵌套在需要返回的节点集之中的 track-listing 元素下所包含的 track 元素节点的个数。最后还需要对这些节点计数,XPath 提供了 count() 函数。此外还需要把这个数字与 10 比较。于是得到了路径:/cds/cd[count(track-listing/track) >= 10]。
关于 XPath 谓词中的矛盾
如果阅读得足够仔细,可能会注意到 XPath 对元素文本和属性的处理是不一致的。前面,我提到属性节点包括属性及其值作为一个信息单位来处理。但是在谓词中,@type 这样的表达式引用的不是整个 type 属性节点而仅仅是属性值。从而能够与其他值进行比较(如 @type='reggae')。
同样,也可在谓词中引用元素文本,如 /cds/cd[title='Revolver']。这里用嵌套在 cd 中的 type 元素的值和 “Revolver” 比较。而且和属性节点一样,违背了看待元素的几条标准规则。一般来说,元素节点是文本节点的父节点,但这里谓词实际上引用了元素中的文本。
这种轻微的规则违背不是大问题,只要您知道有这种情况,而且能够转换对元素和属性的两种不同思考方式即可。只需要明确什么时候将属性及其值看作一个节点,什么时候引用属性值;类似的,也知道什么时候元素包含其他文本节点,什么时候文本值能够和其他值比较。
看看 XQuery
XPath 无疑非常强大,但也有其局限性。最突出的是,它很大程度上只适合静态数据。换句话说,需要针对特定文档编写 XPath 查询,提供和元素、属性、文本比较的具体数据来使用谓词和 XPath。此外,XPath 也没有任何控制结构(如 if/else 语句),除了简单的比较外也不能执行任何处理。
坦白地说,这些限制对多数非程序员来说算不上大问题。但是对于 Java(或者 C#、Python)程序员,习惯了完整的编程语言的强大功能,很快就想到需要 XPath 本身功能之外的方法搜索 XML 文档。于是 XQuery 理所当然地登场了。
选择文档
XQuery 中很少使用但是非常重要的一个特性是能够指定应用 XPath 的文档。前面我们已经将路径 /cds/cd[title='Revolver'] 应用到了一个特定的文档,但在 XQuery 中可使用 doc() 函数指定文档。因此如果搜索 catalog.xml,可使用 XQuery 表达式 doc("catalog.xml")/cds/cd[title='Revolver']。
这个小函数的好处在于可以编写代码来以编程的方式选择文档(比如根据用户的输入),或者遍历一组文档(比如网络中所有的 iTunes 目录)并分别应用该语句。
XQuery 和 FLWOR
当然除了简单地文档选择外,XQuery 还提供了更多的功能。它的 FLWOR 功能尤为强大。FLWOR 是 “for、let、where、order by、return” 的缩写。这都是可用于 XQuery 表达式以便得到更精确结果的子句。
不是 flower
不清楚创造这个缩写词的人为何选择 FLWOR 而不是 FLOWR,第一个没法拼读,第二个看起来像 “flower”。原因很明显,多数表达式中子句的顺序是 for、where、order by 和 return。但还剩下 let,它可以出现在 XQuery 之前或之中。
缩写为 FLWOR 是因为标准化 XML 规范的小组(W3C)选择了它,不过最好不要使用 FLOWR 以免在其他熟悉 XML 的朋友面前丢脸。
对于 SQL 老手来说,应该感到有些熟悉了。 WHERE 和 ORDER BY 都是 SQL 查询中常见的部分。对于程序员来说, for 应该比较眼熟。下面是关于 FLWOR 子句的简要说明:
for:使用 for 遍历节点集。在很多方面,for 就将节点集的当前值赋给变量从而操作该变量。
let:使用 let 可以为变量赋值,但是(很快将看到)和其他 FLWOR 子句相比,let 用的比较少。
where:where 允许向节点集应用选择条件,除了 XPath 原有的机制意外。当然,您将看到在很多查询中 where 并不比 XPath 强,只不过把 XPath 的谓词转移了一个地方。
order by:order by 子句不改变或者筛选数据,仅用于排序结果集,XPath 只能按位置排序,该子句允许按照其他数据排序。
return:使用 return 子句允许您操作操作节点集,随后返回除该节点集以外的结果。有可能在选择节点集、排序并筛选之后,只返回结果的子元素,return 是实现这种功能的关键。
我们再稍微深入地看看这些子句。
使用 for 子句
for 子句的用法和 Java 和 C# 中的用法基本相同。其格式如下:
for $variable-name in XPath
...
其中的变量名可以是任何一般的标识符,如 x。变量一般最好根据用途命名(如 firstName 或r title),不过由于这个变量基本上是一个循环计数器,也可使用单个字母。
XPath 没有限制。/cds/cd 是一个很好的例子。再比如:
for $cd in doc("catalog.xml")/cds/cd
...
如此而已。因此变量 $cd 将取得 XPath /cds/cd 返回的每个节点的值。其中的 ... 代表 XQuery 表达式的其他部分,后面将讲到。
对于程序员来说,这个语句实际上和下面的语句没有区别:
for (int i = 0; i<cdArray.length; i++) {
CD cd = cdArray[i];
// Process CD
}
或者像列表那样:
for (Iterator i = cdList.iterator(); i.hasNext(); ) {
CD cd = (CD)i.next();
// Process each CD
}
使用 let 为变量赋值
先暂缓上面对 XQuery 的讨论,let 子句用于为变量赋值。您可能已经看到,XQuery 中通过在标识符前加上美元符号($)来定义变量。多数情况下按照上述方法使用 XQuery 中的变量,即通过 for 子句隐式建立变量,而不是使用 let 子句显式地定义变量。
不过也有可能需要使用显式的变量。应该这样做:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...
这里将搜索的文档赋给一个变量。XQuery 中的赋值使用 := — 除非曾经用过 Pascal— 可能有点奇特。如果在更真实的环境中,可能用一个函数迭代遍历一组 XML 文档,依次将这些文档名赋给 $docName。这样就能选择每个 文档中的每个 cd 元素,按照同样的方式分别处理。
用 return 子句完成查询
为了完成查询,我们先跳过 FLWOR 中的排序子句。现在我们已经得到了:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...
接下来就要返回一些东西了。查询选择了所有 cd 元素,但是返回这些元素没有多少用处 — 虽然这就是需要的节点集。这里不返回这些元素,我们假设需要某种更容易识别的内容,比如存储在 title 元素中的每张 CD 标题。这就用到 return 了。
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return $cd/title/text()
首先选中了所有的 cd 元素。然后依次将得到的节点赋给 $cd。最后, return 子句返回的不是元素本身,而是包含目标数据的子元素 “title”。
一定不要 犯下面的错误:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return /cds/cd/title/text()
这里有三个明显的问题:
首先它忘记了 XQuery 的目的,返回了一个 XPath 路径而没有执行任何查询。
其次,没有使用 doc($docName) 返回数据,该变量指定了要从中选择 CD 的文档。
最后,也是最重要的,该表达式将忽略对 for 子句返回的节点集所执行的筛选或排序操作。
后面还要用到这样的查询,其重要性将更加明显。目前,先要记住必须保证 for 子句定义的变量同时出现在 return 子句中。这一简单的法则可以保证查询得到预期的结果。
用 where 选择
where 增强了 XQuery 的选择能力。XQuery 中的 where 子句和 SQL 中一样,在选择中增加 where 子句是为了进一步细化结果集。这是一个非常简单的例子:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
return $cd/title/text()
该查询返回所有瑞格舞曲唱片的标题。不需要进一步解释,where 子句非常简单。通过 and 还可建立更加复杂的条件:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
and count($cd/track-listing/track) > 10
return $cd/title/text()
返回包含 10 个曲子以上的所有瑞格唱片。
另一种重要的 where 应用类型是执行连接。比如有一个清单 2 所示的 XML 文件。
清单 2. 扩展后的 CD 清单文档
<cds>
<cd style="some-style">
<title>CD title</title>
<artist id="289" />
<track-listing>
<track>Track title</track>
<!-- More track elements... -->
</track-listing>
</cd>
<!-- More CDs... -->
<artists>
<artist id="289">
<firstName>Bob</firstName>
<lastName>Marley</lastName>
</artist>
<!-- More artist elements -->
</artists>
</cds>
它扩展了 清单 1 所示 XML 文档的结构。增加了 artist 元素,该元素用 id 属性标识,每张 CD 至少有一个 artist 元素嵌套在 cd 元素中。
使用 XQuery 可以实现唱片和艺术家的连接。比如:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
return $cd/title/text()
这里有两点需要注意。您第一次看到 for 子句可以定义多个变量。除了 CD 外,该语句还定义了 $artist,从而处理 artist 元素集合。
其次,where 子句使用 where $cd/artist/$id = $artist/$id 连接了 CD 和艺术家。记住,这样将匹配每张 CD 和每位艺术家,从而得到相当于 SQL 连接的效果。然后进一步选择:$artist/lastName = 'Marley'。从而得到姓氏为 “Marley” 的所有艺术家。但这同时是一个连接,return 子句返回 CD 标题。从而得到了姓氏为 “Marley” 的艺术家的所有唱片的标题。
这正是 XQuery 独有的功能。可以对 XML 文档(很多可能没考虑到进行高级搜索)执行复杂的、类似 SQL 的连接和选择。
排序节点集
如果说 where 和对应的 SQL 结构非常 相似,那么 order by 就是和对应的 SQL 结构完全 相同了。可以根据任何能够通过 XPath 引用的数据对结果排序:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date
return $cd/title/text()
这里返回的 CD 标题按照每张 CD 的子元素 release 的 date 属性排列。默认情况按升序排列,如果愿意也可明确说明:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date ascending
return $cd/title/text()
当然也可按照降序排列。下面的查询首先返回最新发行的唱片:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date descending
return $cd/title/text()
注意:如果没有模式或者使用的 XQuery 处理程序不知道 date 属性是日期类型,该语句可能会出现错误。更糟的是,有可能把日期作为文本数据按照字母顺序来排序。不过基本上所有的现代处理程序都能识别日期,因此很少出现这种情况。
排序还可以包括更多的条件。比如,上面的表达式返回姓氏为 “Marley” 的所有 艺术家(不仅仅是 Bob)的唱片,按照发行日期排序的话不同艺术家的作品就会混在一起。可以进一步改进该表达式,首先按名字然后按发行期排序:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $artist/firstName, $cd/release/@date
return $cd/title/text()
请注意,首先按 $artist/lastName 然后按 $artist/firstName 排序没有必要,因为结果中的姓氏都一样。
引入 Java 技术
为了在 Java 环境中使用 XQuery,前面的介绍似乎太罗嗦了。但是,选择 XQJ(常见的 Java XQuery API 缩写)的多数程序员至少要对 XPath 和 XQuery 有所了解。现在我们介绍的东西比基础稍多一点,可以在 Java 程序中使用这些表达式了。
XQuery 规范是供应商中立的
XQuery for Java API 是在 Sun 的支持下作为 Java Community Process, JSR 225(链接见 参考资料)的一部分开发的。规范本身涉及到多个不同的供应商(包括 Sun、Nokia、BEA、Oracle、Intel 等)和一些知名人士(比如 Jason Hunter,servlet、JDOM 和 XML 方面的名人)。因此,能够避免维系于特定的数据库供应商或者 XML 产品厂家。
但 XQJ 的实现不是中立的
不幸的是,还没有 XQJ 的 Sun 的标准实现。专家组的多数供应商都致力于在其产品中提供 XQJ 实现,这就意味着必须解决某些和特定供应商有关的问题。当然,对于长期使用 XML 的人来说,这和二十世纪初的 XML 解析器与 XSL 处理程序之争没有什么区别。随着时间的推移 XQJ 将标准化,Sun 几乎肯定会发布自己的 XQJ 版本或者 XQJ 实现的包装器 API,就像 XML 解析器与 XSL 处理程序的 JAXP 一样。
获得 XQJ 实现
学习 XQJ 最简单的办法是从 DataDirect 下载免费的试用版。必须填写一份非常烦人的表单,不过此后就可以使用足够长的时间 — 至少能用到本文结束。请访问 DataDirect XQuery 下载站点(链接见 参考资料)。还必须进入需要访问的数据库 — 即使选择 XML Documents Only 选项。配置好这些选项之后,就会得到一封电子邮件说明到哪里下载 JAR 文件 datadirectxquery.jar。
展开 JARred JAR
安装过程有点麻烦,首先需要解压下载的 datadirectxquery.jar。可以使用 jar 命令。但首先要建立安装目录,然后将 JAR 文件解压到该目录。运行 jar 命令:
[bdm0509:~/Desktop] mkdir xqj
[bdm0509:~/Desktop] cd xqj
[bdm0509:~/Desktop/xqj] jar xvf ../datadirectxquery.jar
inflated: XQueryInstaller.jar
inflated: ddxqj.jar
inflated: ExtensionTool.jar
inflated: Readme.txt
inflated: 3rdPartySoftware.txt
inflated: Fixes.txt
inflated: installer.properties
运行安装程序
现在打开新建的目录并双击 XQueryInstaller.jar 文件。如果系统中安装了 Java,将打开 GUI 安装程序。
提示您选择试用版还是注册版,选择 trial。接下来需要选择安装目录。输入的目录一定要具有写文件的权限。我选择的是 /usr/local/java/xqj,首先要保证能够写入 /usr/local/java 目录。安装过程还将建立一个子目录 — 该例中的目录名为 xqj — 然后将 DataDirect XQuery 文件放入该目录。最后运行安装并单击 Finish。
完成之后打开新建目录看看其中的内容,应该如下所示:
[bdm0509:/usr/local/java] cd xqj
[bdm0509:/usr/local/java/xqj] ls
3rdPartySoftware.txt examples lib
Fixes.txt help planExplain
Readme.txt javadoc src
来源:ibm 发布时间:2008-07-24 收藏 投稿 字体:【大 中 小】
XML 数据格式很难搜索,但是最近随着 XQuery API 的出现,XML 搜索变得非常灵活和简单。对于使用 SAX、DOM、JDOM、JAXP 等处理 XML 文档的 Java™ 程序员来说,工具箱中增加 XQuery API for Java 是一件值得高兴的事。现在 Java 程序员不用再求助于系统调用或者笨拙的 API 就能利用 XQuery 的强大功能了,Sun 提供了一套完整的、标准化的包。
SQL 数据库、XML 数据和查询
虽然编程领域 — 特别是 Java 编程 — 不断发展,可供选择的标准的数量也在增长。换句话说,越来越多的 API 得到 Sun 的认可或者批准。标准化的结果是越来越多的开发者背弃了自己最具竞争力的技术,而去学习新技术。
要掌握的最有吸引力和价值的是那些和数据处理有关的工具和 API。无论应用程序多么酷或者智能,说到底只有能够处理数据才有用。同时,虽然 API 数量的不断增长,但流行和通用的数据格式数量却不断减少。虽然有些数据管理员仍然使用面向对象的数据库管理系统或者 XML 驱动的数据库,但关系数据库(RDBMS)已成为主流,而且仍然是大部分数据管理员的选择。因此 Java 开发人员必须通过 JDBC(数据库连接)或者 JDO (Java Data Objects) 与 SQL 数据库交互。
SQL 数据库与其他数据库
虽然本文提到了查询和数据库,但所指的是 SQL 数据库 — 也称为关系数据库。不过 XML 数据库甚至对象数据库都有一些很好的应用程序。
如果对 XML 数据库感兴趣,可以查看 DB2® Express-C,可免费下载(链接参见 参考资料)。最明显的一点是,XML 数据库在 XML 文档和关系数据之间进行不必要的转换。本文还认为,XQuery 实际上已变为数据库查询语言,因为 XML 数据库以 XML 格式存储数据。
数据库之外的数据基本上也都以 XML 作为标准数据格式。XML 虽然冗长但是健壮,Java 语言中处理 XML 的 API 可能比其他语言都多。无论是解析、数据绑定还是转换,如果应用程序不能处理 XML,就会被认为有局限性甚至有点落后。
两者看似无关,— 数据往往保存在 SQL 数据库中,数据库之外的数据越来越多地采用 XML 格式 —,但是却造成了一些独特的问题。SQL 数据库容易查询,而 XML 文档则不是这样。消费者希望能够方便地搜索数据,数据库中的数据查询方便,但是 XML 文档中的数据就不那么容易了。显然,将 XML 格式的数据塞到数据库中以方便搜索的做法是行不通的。于是就出现了 XQuery — 相应的也就有了 XQuery API for Java (XQJ)。
XQuery:三种技术
经常用到的缩写词
API:应用程序编程接口
DOM:文档对象模型
GUI:图形用户接口
IDE:集成开发环境
JAXP:Java API for XML Processing
SAX:Simple API for XML
W3C:万维网联盟
XML:可扩展标记语言
简言之,XQuery 就是用于定义 XML 文档搜索的一种语言。就像 SQL 赋予了 SELECT 和 FROM 特定的含义一样 — 在一定的上下文中 — XQuery 定义了正斜杠(/)和地址符号(@)以及其他一些关键字和关键字符的意义。
XQuery 的核心包括三部分:
XPath 规范:在 XML 文档中选择零个、一个或多个节点的方法。
选择特定 XML 文档、为 XPath 返回的节点增加选择条件的附加语法
API — 比如 XQJ,XQuery for Java API— 用具体的编程语言对 XQuery 表达式求值
要真正精通 XQuery,必须切实掌握好这三个方面。对于 Java 程序员来说,显然就意味着要学习 XPath、学习新增的 XPath 语法结构,然后将其与基于 Java 的 API 结合起来对 XML 文档使用 XQuery 表达式。
好消息是 XPath 和 XQuery 语法都非常简单明了。如果曾经使用 UNIX® shell、Mac OS X 终端或者 DOS 窗口研究过目录结构,就具备了必要的基础。掌握了小于(<)、大于(>)和等于(=)这些运算符的基本用法外,您称得上是大半个 XPath 专家了。
XPath 基础
事实上,XQuery 差不多完全依赖于另一个 XML 规范,即 XPath 规范。XPath 的作用基本上就是定义创建指向 XML 文档某一部分的路径 的方法。比如,XPath /play/act/scene 就是指 play 根元素下的 act 元素下的所有 scene 元素。
XPath 是相对路径
最基本的 XPath 使用元素名和正斜杠。默认情况下,XPath 以 XML 文档中的当前位置开始。因此如果使用 DOM,比如导航到 speech 元素,然后指定路径 speaker,则指向当前位置的 speech 元素中的所有 speaker 元素。因此 XPath 的求值是相对于文档中的位置而言的。
从当前节点移动
要移动到文档的根元素,可在路径前加上正斜杠。无论在文档的什么位置,/play 可以查找名为 play 的根元素。用 ../ 可以选择当前元素的父元素。应该能够看出这实际上非常类似与目录结构。路径 ../../personae/title 将从当前元素上溯两层,然后依次寻找 personae 和嵌套在其中的 title 元素。
选择属性
除了元素外还有很多可供选择的其他内容。比如这个 XPath:/cds/cd@title。它返回根元素 cds 下 cd 元素上所有的 “title” 属性。
要记住,@ 返回的不是属性的值而是属性本身。因此,@isbn 将选择所有名为 isbn 属性而不是这些属性的值。此外,XPath 中的属性指的是属性名和属性值(请参阅后面的 关于节点 一节)。
选择文本
文本嵌套在元素中
对于元素和元素中的文本,通常的想法是认为元素 “具有文本”。经常看到这样的说法,“title 元素的值是 ‘You Can't Count On Me.’”。这是不正确的,实际上文本嵌套在元素中。可以认为文本属于一个元素,但是和属性具有文本值完全不同。
最好的办法是认为文本确实嵌套在元素中。元素是文本的双亲,文本不是它所嵌套的元素的值。
就像选择元素和属性一样,也可选择元素中的文本。如果 XPath 以元素名结束,比如 /cds/cd/title,则选择的是元素 — 而且不 包括这些元素中的文本。如果需要元素中的文本,可使用 text() 语法。因此要取得所有 CD 的文字标题,可以使用 /cds/cd/title/text() 这样的路径。该路径不提供任何元素,而是指定元素中的文本。
XPath 选择一组节点
有效使用 XPath 的一个关键是要认识到 XPath 始终计算的是节点集。这个集合可能包含零个、一个或多个节点,但 XPath 的结果必定 是集合。多数人在编写 XPath 的时候认为:该路径返回一个元素、一个属性或者文本,但事实并非如此。不过这也不是绝对的。
如果使用 DOM,应该已经对节点有所了解了。对于 DOM 来说,XML 文档中的一切 — 元素、属性和文本 — 都是节点。元素有元素节点,属性(包括属性值)有属性节点,元素中的文本则是文本节点。因此路径 ../../personae/title 最终将选择 title 元素,实际上返回一个节点集。这个集合可能包含零个(没有匹配的元素)、一个或者多个节点。集合中的所有节点都是名为 “title” 的元素。
随着路径变得更加复杂,有可能选择一个更大的集合 — 也许同时包括属性和元素,或者同时包括文本和元素 — 无论如何这些路径最终都是选择一个节点集。牢记这一点是正确使用 XQuery 的关键。通过 XPath 选择一个节点集,而使用 XQuery 则通常是按照一定条件选择这些节点的一个子集,也有可能连接多个节点集然后应用搜索条件。只要记住集合中的元素可能有多种类型(元素、文本或者属性),就能更好地编写路径并保证得到预期的结果。
增加 XPath 的选择性
前面我们看到了如何根据节点名(元素和属性)以及父节点(文本或者选择给定节点的所有子节点)来选择节点集。这本身已经非常强大了,不过 XPath 还提供了更多的选择性,即使用所谓的谓词。
谓词语法
Microsoft® Internet Explorer®(及其他一些浏览器)的错误
XPath 规范要求,谓词 [1] 指向集合的第一个节点,换句话说,XPath 节点集的索引是从 1 开始的。但是,Internet Explorer 多数版本的 XPath 实现都错误地采用零基数索引,[1] 指向数组中的第二 项,[0] 表示第一项。您需要在浏览器中测试,但集合中第一项的索引为 [1] 而不是 [0],这才是正确的。
谓词是用于已有节点集的部分表达式。谓词放在方括号 [ 和 ] 中。作用于谓词左侧的路径所定义的节点集。比如路径 /cds/cd 选择了根元素 “cds” 中的所有 cd 元素。假设需要第一张唱片,可以使用谓词来实现:/cds/cd[1]。它返回路径 /cds/cd 选择的第一个节点。
谓词可用于任何节点集
要记住,谓词作用于谓词本身左侧的节点集。但是这并不意味着谓词只能出现在 XPath 的最后面。可以将 XPath 本身看作是路径的集合,每个路径都返回一个节点集,路径后面的部分作用于该集合并进一步细化。因此 /cds/cd/title 实际上是三个路径:
/cds 返回根元素 “cds”(只含一个元素节点的集合)
cd(相对于上一个节点集)返回上一节点集中嵌套的所有 cd 元素
title(同样相对于上一节点集)返回上一节点集中嵌套的所有 title 元素
谓词必须作用于节点集,但是可作用于任何 节点集。/cds[1]/cd[2]/title[1] 这样的路径是完全合法的。它选择了 /cds 所选节点集中的第一项,/cds[1]/cd 所选节点的第二项,/cds[1]/cd[2]/title 所选节点的第一项。
注意:该路径中有些部分毫无意义,比如使用 / 选择根元素然后再使用 [1] 谓词总是返回集合中的第一个(惟一的一个)元素。只有当节点集本身为空,即给出的根元素名称和文档实际的根元素不同,这种情况下才不会返回任何元素。但是作为一个例子 — 包括从技术的角度看 — XPath 本身以及使用的谓词都没有问题。
超越谓词中的数值索引
当然,只能用数值引用位置的 API 作用是很有限的。这样的模型中,您必须知道需要的项到底在什么位置。不过 XPath 提供了更多的选择。首先,在谓词中可以使用 last() 函数选择集合中的最后一项,不论集合中包含多少项。/cds/cd[last] 选择文档中最后一个 cd。
使用 position() 函数还可以选择在特定位置之前或者之后的所有项。position() 函数返回给定节点在集合中的位置。比如,假设需要前 5 张唱片,可以使用路径 /cds/cd[position()<6]。这样就能选择 position() 结果小于 6 的所有节点。
根据数据选择节点
最后 — 对于这里关于 XPath 的简要介绍而不是详细讨论来说 — 还可以根据节点的子元素或者节点的属性来选择节点。就像 XPath 后面的部分依赖于前面路径所决定的节点集一样,集合的谓词也依赖于所应用的集合。谓词除了能够使用小于(<)和大于(>)这样的运算符外,还可根据选中节点的数据而不仅仅是这些节点在整个集合中的位置进行选择。
比方说要选取属性 “style” 为 “folk” 的所有 CD。表达式应该首先选择全部 CD,然后把这些 CD 的 style 属性和 “folk” 进行比较。其 XPath 形式为 /cds/cd[@style='folk']。按照前面的说明,这个表达式的意思应该很清楚了。首先,通过 /cds/cd 选择了一个节点集。然后使用谓词 @style 取出每个节点的 “style” 属性。前面已经提到,@ 表示属性。并且该属性是关于已经选择的节点集的(即所有 cd 元素)。接下来把这些属性的值和字符串 “folk” 比较。属性匹配的返回,其他的则在结果集中排除掉。
该方法也可用于所选集合的嵌套元素。假设文档结构如清单 1 所示。
清单 1. CD 清单文档的结构
<cds>
<cd style="some-style">
<title>CD title</title>
<track-listing>
<track>Track title</track>
<!-- More track elements... -->
</track-listing>
</cd>
<!-- More CDs... -->
</cds>
比方说如果希望取得包含 10 条或更多音轨的 CD。首先要选择全部 CD 元素,即前面多次用到的路径:/cds/cd。然后使用谓词得到关于所选集合的特定节点集的项数,即嵌套在需要返回的节点集之中的 track-listing 元素下所包含的 track 元素节点的个数。最后还需要对这些节点计数,XPath 提供了 count() 函数。此外还需要把这个数字与 10 比较。于是得到了路径:/cds/cd[count(track-listing/track) >= 10]。
关于 XPath 谓词中的矛盾
如果阅读得足够仔细,可能会注意到 XPath 对元素文本和属性的处理是不一致的。前面,我提到属性节点包括属性及其值作为一个信息单位来处理。但是在谓词中,@type 这样的表达式引用的不是整个 type 属性节点而仅仅是属性值。从而能够与其他值进行比较(如 @type='reggae')。
同样,也可在谓词中引用元素文本,如 /cds/cd[title='Revolver']。这里用嵌套在 cd 中的 type 元素的值和 “Revolver” 比较。而且和属性节点一样,违背了看待元素的几条标准规则。一般来说,元素节点是文本节点的父节点,但这里谓词实际上引用了元素中的文本。
这种轻微的规则违背不是大问题,只要您知道有这种情况,而且能够转换对元素和属性的两种不同思考方式即可。只需要明确什么时候将属性及其值看作一个节点,什么时候引用属性值;类似的,也知道什么时候元素包含其他文本节点,什么时候文本值能够和其他值比较。
看看 XQuery
XPath 无疑非常强大,但也有其局限性。最突出的是,它很大程度上只适合静态数据。换句话说,需要针对特定文档编写 XPath 查询,提供和元素、属性、文本比较的具体数据来使用谓词和 XPath。此外,XPath 也没有任何控制结构(如 if/else 语句),除了简单的比较外也不能执行任何处理。
坦白地说,这些限制对多数非程序员来说算不上大问题。但是对于 Java(或者 C#、Python)程序员,习惯了完整的编程语言的强大功能,很快就想到需要 XPath 本身功能之外的方法搜索 XML 文档。于是 XQuery 理所当然地登场了。
选择文档
XQuery 中很少使用但是非常重要的一个特性是能够指定应用 XPath 的文档。前面我们已经将路径 /cds/cd[title='Revolver'] 应用到了一个特定的文档,但在 XQuery 中可使用 doc() 函数指定文档。因此如果搜索 catalog.xml,可使用 XQuery 表达式 doc("catalog.xml")/cds/cd[title='Revolver']。
这个小函数的好处在于可以编写代码来以编程的方式选择文档(比如根据用户的输入),或者遍历一组文档(比如网络中所有的 iTunes 目录)并分别应用该语句。
XQuery 和 FLWOR
当然除了简单地文档选择外,XQuery 还提供了更多的功能。它的 FLWOR 功能尤为强大。FLWOR 是 “for、let、where、order by、return” 的缩写。这都是可用于 XQuery 表达式以便得到更精确结果的子句。
不是 flower
不清楚创造这个缩写词的人为何选择 FLWOR 而不是 FLOWR,第一个没法拼读,第二个看起来像 “flower”。原因很明显,多数表达式中子句的顺序是 for、where、order by 和 return。但还剩下 let,它可以出现在 XQuery 之前或之中。
缩写为 FLWOR 是因为标准化 XML 规范的小组(W3C)选择了它,不过最好不要使用 FLOWR 以免在其他熟悉 XML 的朋友面前丢脸。
对于 SQL 老手来说,应该感到有些熟悉了。 WHERE 和 ORDER BY 都是 SQL 查询中常见的部分。对于程序员来说, for 应该比较眼熟。下面是关于 FLWOR 子句的简要说明:
for:使用 for 遍历节点集。在很多方面,for 就将节点集的当前值赋给变量从而操作该变量。
let:使用 let 可以为变量赋值,但是(很快将看到)和其他 FLWOR 子句相比,let 用的比较少。
where:where 允许向节点集应用选择条件,除了 XPath 原有的机制意外。当然,您将看到在很多查询中 where 并不比 XPath 强,只不过把 XPath 的谓词转移了一个地方。
order by:order by 子句不改变或者筛选数据,仅用于排序结果集,XPath 只能按位置排序,该子句允许按照其他数据排序。
return:使用 return 子句允许您操作操作节点集,随后返回除该节点集以外的结果。有可能在选择节点集、排序并筛选之后,只返回结果的子元素,return 是实现这种功能的关键。
我们再稍微深入地看看这些子句。
使用 for 子句
for 子句的用法和 Java 和 C# 中的用法基本相同。其格式如下:
for $variable-name in XPath
...
其中的变量名可以是任何一般的标识符,如 x。变量一般最好根据用途命名(如 firstName 或r title),不过由于这个变量基本上是一个循环计数器,也可使用单个字母。
XPath 没有限制。/cds/cd 是一个很好的例子。再比如:
for $cd in doc("catalog.xml")/cds/cd
...
如此而已。因此变量 $cd 将取得 XPath /cds/cd 返回的每个节点的值。其中的 ... 代表 XQuery 表达式的其他部分,后面将讲到。
对于程序员来说,这个语句实际上和下面的语句没有区别:
for (int i = 0; i<cdArray.length; i++) {
CD cd = cdArray[i];
// Process CD
}
或者像列表那样:
for (Iterator i = cdList.iterator(); i.hasNext(); ) {
CD cd = (CD)i.next();
// Process each CD
}
使用 let 为变量赋值
先暂缓上面对 XQuery 的讨论,let 子句用于为变量赋值。您可能已经看到,XQuery 中通过在标识符前加上美元符号($)来定义变量。多数情况下按照上述方法使用 XQuery 中的变量,即通过 for 子句隐式建立变量,而不是使用 let 子句显式地定义变量。
不过也有可能需要使用显式的变量。应该这样做:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...
这里将搜索的文档赋给一个变量。XQuery 中的赋值使用 := — 除非曾经用过 Pascal— 可能有点奇特。如果在更真实的环境中,可能用一个函数迭代遍历一组 XML 文档,依次将这些文档名赋给 $docName。这样就能选择每个 文档中的每个 cd 元素,按照同样的方式分别处理。
用 return 子句完成查询
为了完成查询,我们先跳过 FLWOR 中的排序子句。现在我们已经得到了:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
...
接下来就要返回一些东西了。查询选择了所有 cd 元素,但是返回这些元素没有多少用处 — 虽然这就是需要的节点集。这里不返回这些元素,我们假设需要某种更容易识别的内容,比如存储在 title 元素中的每张 CD 标题。这就用到 return 了。
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return $cd/title/text()
首先选中了所有的 cd 元素。然后依次将得到的节点赋给 $cd。最后, return 子句返回的不是元素本身,而是包含目标数据的子元素 “title”。
一定不要 犯下面的错误:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
return /cds/cd/title/text()
这里有三个明显的问题:
首先它忘记了 XQuery 的目的,返回了一个 XPath 路径而没有执行任何查询。
其次,没有使用 doc($docName) 返回数据,该变量指定了要从中选择 CD 的文档。
最后,也是最重要的,该表达式将忽略对 for 子句返回的节点集所执行的筛选或排序操作。
后面还要用到这样的查询,其重要性将更加明显。目前,先要记住必须保证 for 子句定义的变量同时出现在 return 子句中。这一简单的法则可以保证查询得到预期的结果。
用 where 选择
where 增强了 XQuery 的选择能力。XQuery 中的 where 子句和 SQL 中一样,在选择中增加 where 子句是为了进一步细化结果集。这是一个非常简单的例子:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
return $cd/title/text()
该查询返回所有瑞格舞曲唱片的标题。不需要进一步解释,where 子句非常简单。通过 and 还可建立更加复杂的条件:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd
where $cd@type = 'reggae'
and count($cd/track-listing/track) > 10
return $cd/title/text()
返回包含 10 个曲子以上的所有瑞格唱片。
另一种重要的 where 应用类型是执行连接。比如有一个清单 2 所示的 XML 文件。
清单 2. 扩展后的 CD 清单文档
<cds>
<cd style="some-style">
<title>CD title</title>
<artist id="289" />
<track-listing>
<track>Track title</track>
<!-- More track elements... -->
</track-listing>
</cd>
<!-- More CDs... -->
<artists>
<artist id="289">
<firstName>Bob</firstName>
<lastName>Marley</lastName>
</artist>
<!-- More artist elements -->
</artists>
</cds>
它扩展了 清单 1 所示 XML 文档的结构。增加了 artist 元素,该元素用 id 属性标识,每张 CD 至少有一个 artist 元素嵌套在 cd 元素中。
使用 XQuery 可以实现唱片和艺术家的连接。比如:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
return $cd/title/text()
这里有两点需要注意。您第一次看到 for 子句可以定义多个变量。除了 CD 外,该语句还定义了 $artist,从而处理 artist 元素集合。
其次,where 子句使用 where $cd/artist/$id = $artist/$id 连接了 CD 和艺术家。记住,这样将匹配每张 CD 和每位艺术家,从而得到相当于 SQL 连接的效果。然后进一步选择:$artist/lastName = 'Marley'。从而得到姓氏为 “Marley” 的所有艺术家。但这同时是一个连接,return 子句返回 CD 标题。从而得到了姓氏为 “Marley” 的艺术家的所有唱片的标题。
这正是 XQuery 独有的功能。可以对 XML 文档(很多可能没考虑到进行高级搜索)执行复杂的、类似 SQL 的连接和选择。
排序节点集
如果说 where 和对应的 SQL 结构非常 相似,那么 order by 就是和对应的 SQL 结构完全 相同了。可以根据任何能够通过 XPath 引用的数据对结果排序:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date
return $cd/title/text()
这里返回的 CD 标题按照每张 CD 的子元素 release 的 date 属性排列。默认情况按升序排列,如果愿意也可明确说明:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date ascending
return $cd/title/text()
当然也可按照降序排列。下面的查询首先返回最新发行的唱片:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $cd/release/@date descending
return $cd/title/text()
注意:如果没有模式或者使用的 XQuery 处理程序不知道 date 属性是日期类型,该语句可能会出现错误。更糟的是,有可能把日期作为文本数据按照字母顺序来排序。不过基本上所有的现代处理程序都能识别日期,因此很少出现这种情况。
排序还可以包括更多的条件。比如,上面的表达式返回姓氏为 “Marley” 的所有 艺术家(不仅仅是 Bob)的唱片,按照发行日期排序的话不同艺术家的作品就会混在一起。可以进一步改进该表达式,首先按名字然后按发行期排序:
let $docName := 'catalog.xml'
for $cd in doc($docName)/cds/cd,
$artist in doc($docName)/cds/artists/artist
where $cd/artist/$id = $artist/$id
and $artist/lastName = 'Marley'
order by $artist/firstName, $cd/release/@date
return $cd/title/text()
请注意,首先按 $artist/lastName 然后按 $artist/firstName 排序没有必要,因为结果中的姓氏都一样。
引入 Java 技术
为了在 Java 环境中使用 XQuery,前面的介绍似乎太罗嗦了。但是,选择 XQJ(常见的 Java XQuery API 缩写)的多数程序员至少要对 XPath 和 XQuery 有所了解。现在我们介绍的东西比基础稍多一点,可以在 Java 程序中使用这些表达式了。
XQuery 规范是供应商中立的
XQuery for Java API 是在 Sun 的支持下作为 Java Community Process, JSR 225(链接见 参考资料)的一部分开发的。规范本身涉及到多个不同的供应商(包括 Sun、Nokia、BEA、Oracle、Intel 等)和一些知名人士(比如 Jason Hunter,servlet、JDOM 和 XML 方面的名人)。因此,能够避免维系于特定的数据库供应商或者 XML 产品厂家。
但 XQJ 的实现不是中立的
不幸的是,还没有 XQJ 的 Sun 的标准实现。专家组的多数供应商都致力于在其产品中提供 XQJ 实现,这就意味着必须解决某些和特定供应商有关的问题。当然,对于长期使用 XML 的人来说,这和二十世纪初的 XML 解析器与 XSL 处理程序之争没有什么区别。随着时间的推移 XQJ 将标准化,Sun 几乎肯定会发布自己的 XQJ 版本或者 XQJ 实现的包装器 API,就像 XML 解析器与 XSL 处理程序的 JAXP 一样。
获得 XQJ 实现
学习 XQJ 最简单的办法是从 DataDirect 下载免费的试用版。必须填写一份非常烦人的表单,不过此后就可以使用足够长的时间 — 至少能用到本文结束。请访问 DataDirect XQuery 下载站点(链接见 参考资料)。还必须进入需要访问的数据库 — 即使选择 XML Documents Only 选项。配置好这些选项之后,就会得到一封电子邮件说明到哪里下载 JAR 文件 datadirectxquery.jar。
展开 JARred JAR
安装过程有点麻烦,首先需要解压下载的 datadirectxquery.jar。可以使用 jar 命令。但首先要建立安装目录,然后将 JAR 文件解压到该目录。运行 jar 命令:
[bdm0509:~/Desktop] mkdir xqj
[bdm0509:~/Desktop] cd xqj
[bdm0509:~/Desktop/xqj] jar xvf ../datadirectxquery.jar
inflated: XQueryInstaller.jar
inflated: ddxqj.jar
inflated: ExtensionTool.jar
inflated: Readme.txt
inflated: 3rdPartySoftware.txt
inflated: Fixes.txt
inflated: installer.properties
运行安装程序
现在打开新建的目录并双击 XQueryInstaller.jar 文件。如果系统中安装了 Java,将打开 GUI 安装程序。
提示您选择试用版还是注册版,选择 trial。接下来需要选择安装目录。输入的目录一定要具有写文件的权限。我选择的是 /usr/local/java/xqj,首先要保证能够写入 /usr/local/java 目录。安装过程还将建立一个子目录 — 该例中的目录名为 xqj — 然后将 DataDirect XQuery 文件放入该目录。最后运行安装并单击 Finish。
完成之后打开新建目录看看其中的内容,应该如下所示:
[bdm0509:/usr/local/java] cd xqj
[bdm0509:/usr/local/java/xqj] ls
3rdPartySoftware.txt examples lib
Fixes.txt help planExplain
Readme.txt javadoc src
相关推荐
内容概要:本文详细介绍了欧姆龙NJ系列PLC与多个品牌总线设备(如汇川伺服、雷赛步进控制器、SMC电缸等)的控制程序及其配置方法。重点讨论了PDO映射、参数配置、单位转换、故障排查等方面的实际经验和常见问题。文中提供了具体的代码示例,帮助读者理解和掌握这些复杂系统的调试技巧。此外,还特别强调了不同品牌设备之间的兼容性和注意事项,以及如何避免常见的配置错误。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要进行PLC与总线设备集成工作的专业人士。 使用场景及目标:适用于需要将欧姆龙NJ PLC与其他品牌总线设备集成在一起的应用场景,如工厂自动化生产线、机器人控制等。主要目标是提高系统的可靠性和效率,减少调试时间和成本。 其他说明:文章不仅提供了理论知识,还包括大量来自实际项目的实践经验,有助于读者更好地应对现实中的挑战。建议读者在实践中不断积累经验,逐步掌握各种设备的特点和最佳实践。
数字化企业转型大数据解决方案.pptx
内容概要:本文详细介绍了利用MATLAB实现多智能体系统一致性算法在电力系统分布式经济调度中的应用。文中通过具体的MATLAB代码展示了如何将发电机组和柔性负荷视为智能体,通过局部通信和协商达成全局最优调度。核心算法通过迭代更新增量成本和增量效益,使各个节点在无中央指挥的情况下自行调整功率,最终实现经济最优分配。此外,文章还讨论了通信拓扑对收敛速度的影响以及一些工程优化技巧,如稀疏矩阵存储和自适应参数调整。 适合人群:从事电力系统调度、分布式控制系统设计的研究人员和技术人员,尤其是对多智能体系统和MATLAB编程有一定了解的人群。 使用场景及目标:适用于希望提高电力系统调度效率、降低成本并增强系统鲁棒性的应用场景。主要目标是在分布式环境下实现快速、稳定的经济调度,同时减少通信量和计算资源消耗。 其他说明:文章提供了详细的代码示例和测试结果,展示了算法的实际性能和优势。对于进一步研究和实际应用具有重要参考价值。
获取虎牙直播流地址的油猴脚本,可以直接使用VLC等播放器打开地址播放。
内容概要:本文详细介绍了如何利用MATLAB进行价格型需求响应的研究,特别是电价弹性矩阵的构建与优化。文章首先解释了电价弹性矩阵的概念及其重要性,接着展示了如何通过MATLAB代码实现弹性矩阵的初始化、负荷变化量的计算以及优化方法。文中还讨论了如何通过非线性约束和目标函数最小化峰谷差,确保用户用电舒适度的同时实现负荷的有效调节。此外,文章提供了具体的代码实例,包括原始负荷曲线与优化后负荷曲线的对比图,以及基于历史数据的参数优化方法。 适合人群:从事电力系统优化、能源管理及相关领域的研究人员和技术人员。 使用场景及目标:适用于希望深入了解并掌握价格型需求响应机制的专业人士,旨在帮助他们更好地理解和应用电价弹性矩阵,优化电力系统的负荷分布,提高能源利用效率。 其他说明:文章强调了实际应用中的注意事项,如弹性矩阵的动态校准和用户价格敏感度的滞后效应,提供了实用的技术细节和实践经验。
CSP-J 2021 初赛真题.pdf
内容概要:本文详细介绍了如何利用麻雀优化算法(SSA)与长短期记忆网络(LSTM)相结合,在MATLAB环境中构建一个用于时间序列单输入单输出预测的模型。首先简述了SSA和LSTM的基本原理,接着逐步讲解了从数据准备、预处理、模型构建、参数优化到最后的预测与结果可视化的完整流程。文中提供了详细的MATLAB代码示例,确保读者能够轻松复现实验。此外,还讨论了一些关键参数的选择方法及其对模型性能的影响。 适合人群:对时间序列预测感兴趣的科研人员、研究生以及有一定编程基础的数据分析师。 使用场景及目标:适用于需要对单变量时间序列数据进行高精度预测的应用场合,如金融、能源等领域。通过本篇文章的学习,读者将掌握如何使用MATLAB实现SSA优化LSTM模型的具体步骤和技术要点。 其他说明:为了提高模型的泛化能力,文中特别强调了数据预处理的重要性,并给出了具体的实现方式。同时,针对可能出现的问题,如过拟合、梯度爆炸等,也提供了一些建议性的解决方案。
内容概要:本文详细介绍了西门子S7-1200 PLC与施耐德ATV310/312变频器通过Modbus RTU进行通讯的具体实现步骤和调试技巧。主要内容涵盖硬件接线、通讯参数配置、控制启停、设定频率、读取运行参数的方法以及常见的调试问题及其解决方案。文中提供了具体的代码示例,帮助读者理解和实施通讯程序。此外,还强调了注意事项,如地址偏移量、数据格式转换和超时匹配等。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要将西门子PLC与施耐德变频器进行集成的工作人员。 使用场景及目标:适用于需要通过Modbus RTU协议实现PLC与变频器通讯的工程项目。目标是确保通讯稳定可靠,掌握解决常见问题的方法,提高调试效率。 其他说明:文中提到的实际案例和调试经验有助于读者避免常见错误,快速定位并解决问题。建议读者在实践中结合提供的代码示例和调试工具进行操作。
本文详细介绍了Scala语言的基础知识和特性。Scala是一种运行在JVM上的编程语言,兼具面向对象和函数式编程的特点,适合大数据处理。其环境配置需注意Java版本和路径问题。语言基础涵盖注释、变量、数据类型、运算符和流程控制。函数特性包括高阶函数、柯里化、闭包、尾递归等。面向对象方面,Scala支持继承、抽象类、特质等,并通过包、类和对象实现代码组织和管理,同时提供了单例对象和伴生对象的概念。
内容概要:本文详细探讨了石墨烯-金属强耦合拉比分裂现象的研究,主要借助Comsol多物理场仿真软件进行模拟。文章首先介绍了拉比分裂的基本概念,即当石墨烯与金属相互靠近时,原本单一的共振模式会分裂成两个,这种现象背后的电磁学和量子力学原理对于开发新型光电器件、高速通信设备等意义重大。接着阐述了Comsol在研究中的重要作用,包括构建石墨烯-金属相互作用模型、设置材料属性、定义边界条件、划分网格以及求解模型的具体步骤。此外,还展示了具体的建模示例代码,并对模拟结果进行了深入分析,解释了拉比分裂现象的形成机理。最后强调了该研究对未来技术创新的重要价值。 适合人群:从事物理学、材料科学、光电工程等领域研究的专业人士,尤其是对石墨烯-金属强耦合感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解石墨烯-金属强耦合拉比分裂现象的研究人员,旨在帮助他们掌握Comsol仿真工具的应用技巧,提高研究效率,推动相关领域的创新发展。 其他说明:文中提供的代码片段和建模思路可供读者参考实践,但需要注意实际应用时需根据具体情况调整参数配置。
内容概要:本文详细介绍了基于FPGA的电机控制系统的设计与实现,重点探讨了Verilog和Nios II软核相结合的方式。具体来说,编码器模块利用Verilog实现了高精度的四倍频计数,解决了AB相信号的跳变问题;坐标变换部分则由Nios II软核负责,通过C语言实现Clarke变换和Park变换,提高了计算效率;SVPWM生成模块采用了Verilog硬件加速,优化了调制波的生成时间和波形质量。此外,文章还讨论了Nios II和Verilog之间的高效交互方式,如自定义指令和DMA传输,以及中断处理机制,确保系统的实时性和稳定性。文中提到的一些优化技巧,如定点数运算、查表法、流水线设计等,进一步提升了系统的性能。 适合人群:具有一定FPGA和嵌入式开发经验的研发人员,尤其是对电机控制感兴趣的工程师。 使用场景及目标:适用于需要高性能、低延迟的电机控制应用场景,如工业自动化、机器人、无人机等领域。目标是帮助读者掌握FPGA与Nios II结合的电机控制方法,提高系统的实时性和可靠性。 其他说明:文章提供了详细的代码片段和优化建议,有助于读者理解和实践。同时,文中提及了一些常见的调试问题及其解决方案,如符号位处理不当导致的电机反转、数据溢出等问题,提醒读者在实际项目中加以注意。
内容概要:本文档《ATK-DLRK3568嵌入式Qt开发实战V1.2》是正点原子出品的一份面向初学者的嵌入式Qt开发指南,主要内容涵盖嵌入式Linux环境下Qt的安装配置、C++基础、Qt基础、多线程编程、网络编程、多媒体开发、数据库操作以及项目实战案例。文档从最简单的“Hello World”程序开始,逐步引导读者熟悉Qt开发环境的搭建、常用控件的使用、信号与槽机制、UI设计、数据处理等关键技术点。此外,文档还提供了详细的项目实战案例,如车牌识别系统的开发,帮助读者将理论知识应用于实际项目中。 适合人群:具备一定Linux和C++基础,希望快速入门嵌入式Qt开发的初学者或有一定开发经验的研发人员。 使用场景及目标: 1. **环境搭建**:学习如何在Ubuntu环境下搭建Qt开发环境,包括安装必要的工具和库。 2. **基础知识**:掌握C++面向对象编程、Qt基础控件的使用、信号与槽机制等核心概念。 3. **高级功能**:理解多线程编程、网络通信、多媒体处理、数据库操作等高级功能的实现方法。 4. **项目实战**:通过具体的项目案例(如车牌识别系统),巩固
内容概要:文章深入探讨了宇树科技人形机器人的技术实力、市场表现及未来前景,揭示其背后是科技创新还是市场炒作。宇树科技,成立于2016年,由90后创业者王兴兴创办,从四足机器人(如Laikago、AlienGo、A1)成功跨越到人形机器人(如H1和G1)。H1具有出色的运动能力和高精度导航技术,G1则专注于娱乐陪伴场景,具备模拟人手操作的能力。市场方面,宇树科技人形机器人因春晚表演而走红,但目前仅限于“极客型”用户购买,二手市场租赁价格高昂。文章认为,宇树科技的成功既源于技术突破,也离不开市场炒作的影响。未来,宇树科技将在工业、服务业、娱乐等多个领域拓展应用,但仍需克服成本、稳定性和安全等方面的挑战。 适合人群:对人工智能和机器人技术感兴趣的科技爱好者、投资者以及相关行业的从业者。 使用场景及目标:①了解宇树科技人形机器人的技术特点和发展历程;②分析其市场表现及未来应用前景;③探讨科技创新与市场炒作之间的关系。 阅读建议:本文详细介绍了宇树科技人形机器人的技术细节和市场情况,读者应关注其技术创新点,同时理性看待市场炒作现象,思考人形机器人的实际应用价值和发展潜力。
C#3-的核心代码以及练习题相关
内容概要:本文详细介绍了一种将麻雀搜索算法(SSA)用于优化支持向量机(SVM)分类的方法,并以红酒数据集为例进行了具体实现。首先介绍了数据预处理步骤,包括从Excel读取数据并进行特征和标签的分离。接着阐述了适应度函数的设计,采用五折交叉验证计算准确率作为评价标准。然后深入探讨了麻雀算法的核心迭代过程,包括参数初始化、种群更新规则以及如何通过指数衰减和随机扰动来提高搜索效率。此外,文中还提到了一些实用技巧,如保存最优参数以避免重复计算、利用混淆矩阵可视化分类结果等。最后给出了完整的代码框架及其在GitHub上的开源地址。 适合人群:具有一定MATLAB编程基础的研究人员和技术爱好者,尤其是对机器学习算法感兴趣的人士。 使用场景及目标:适用于需要解决多分类问题的数据科学家或工程师,旨在提供一种高效且易于使用的SVM参数优化方法,帮助用户获得更高的分类准确性。 其他说明:该方法不仅限于红酒数据集,在其他类似的数据集中同样适用。用户只需确保数据格式正确即可轻松替换数据源。
内容概要:本文详细介绍了如何在MATLAB/Simulink环境中搭建四分之一车被动悬架双质量(二自由度)模型。该模型主要用于研究车辆悬架系统在垂直方向上的动态特性,特别是针对路面不平度引起的车轮和车身振动。文中不仅提供了具体的建模步骤,包括输入模块、模型主体搭建和输出模块的设计,还展示了如何通过仿真分析来评估悬架性能,如乘坐舒适性和轮胎接地性。此外,文章还讨论了一些常见的建模技巧和注意事项,如选择合适的求解器、处理代数环等问题。 适合人群:从事汽车动力学研究的科研人员、高校学生以及对车辆悬架系统感兴趣的工程师。 使用场景及目标:①用于教学目的,帮助学生理解车辆悬架系统的理论知识;②用于科研实验,验证不同的悬架设计方案;③用于工业应用,优化实际车辆的悬架系统设计。 其他说明:本文提供的模型基于MATLAB 2016b及以上版本,确保读者能够顺利重现所有步骤并获得预期结果。同时,文中附带了大量的代码片段和具体的操作指南,便于读者快速上手。
内容概要:本文详细介绍了如何使用COMSOL软件进行光子晶体板谷态特性的建模与仿真。首先,定义了晶格常数和其他关键参数,如六边形蜂窝结构的创建、材料属性的设定以及周期性边界的配置。接下来,重点讲解了网格剖分的方法,强调了自适应网格和边界层细化的重要性。随后,讨论了如何通过参数扫描和频域分析来探索谷态特征,特别是在布里渊区高对称点附近观察到的能量带隙和涡旋结构。最后,提供了关于仿真收敛性和优化技巧的建议,确保结果的可靠性和准确性。 适合人群:从事光子学、电磁学及相关领域的研究人员和技术人员,尤其是对拓扑光子学感兴趣的学者。 使用场景及目标:适用于希望深入了解光子晶体板谷态特性的科研工作者,旨在帮助他们掌握COMSOL的具体应用方法,从而更好地进行相关实验和理论研究。 其他说明:文中不仅提供了详细的代码示例,还穿插了许多形象生动的比喻,使复杂的物理概念变得通俗易懂。同时,强调了仿真过程中需要注意的技术细节,如网格划分、边界条件设置等,有助于避免常见错误并提高仿真的成功率。
内容概要:本文详细介绍了利用有限差分时域法(FDTD)对金纳米球进行米氏散射仿真的全过程。首先,通过Python脚本设置了仿真环境,包括网格精度、材料参数、光源配置等。接着,展示了如何通过近场积分计算散射截面和吸收截面,并进行了远场角分布的仿真。文中还讨论了常见错误及其解决方法,如网格精度不足、边界条件不当等问题。最终,将仿真结果与米氏解析解进行了对比验证,确保了仿真的准确性。 适合人群:从事微纳光学研究的科研人员、研究生以及相关领域的工程师。 使用场景及目标:适用于需要精确模拟纳米颗粒与电磁波相互作用的研究项目,旨在提高仿真精度并验证理论模型。通过本文的学习,可以掌握FDTD仿真的具体实施步骤和技术要点。 其他说明:本文不仅提供了详细的代码示例,还分享了许多实践经验,帮助读者避免常见的仿真陷阱。同时强调了参数选择的重要性,特别是在纳米尺度下,每一个参数都需要精心调整以获得准确的结果。
基数
2ddddddddddddddddddddddddddd