不论你什么时候试图理解一种语言或文件格式的结构,能够阅读BNF都很重要。元符号"::="表示"被定义为","|"表示"或",修饰符"?"、"+"和"*"分别表示0或1、1或更多以及0或更多。
该BNF表明每个模块由一个MainModule或一个LibraryModule组成。MainModule是一个后面带QueryBody的序,而LibraryModule则是一个ModuleDecl,其后为一个不带QueryBody的序。ModuleDecl由字符串"module"打头,然后是StringLiteral,本例中它恰好是一个URI。一个序由一个可选的初始版本以及其后的各种声明、导入和多个顺序不限的定义组成。在这之后是数量不限的函数定义。最后,以前在MainModule中引用的QueryBody被定义为一个单一的Expr表达式。Expr以及其他非终结符号的定义可以在规范的其他地方找到。
我在这里说明BNF是因为这是理解新的查询文件结构最准确的方法,并且当有些查询不能像期望的那样运行而不得不求助于规范时,它还会提供极为有用的帮助。
在序中首先应该是Version(版本)。一个XQuery模块可以在序的开始部分声明它的版本号。Version指明了XQuery的版本,根据这个版本设计代码来运行查询。当遇到一个未知的版本时,处理程序可以抛出一个错误。没有版本声明语句则隐含额版本号为"1.0"。遗憾的是,众多预发布的草案不能使用版本,所以该特性现在还不会提供什么帮助。版本声明类似于:
module "http://www.w3.org/2003/05/
xpath-functions"
xquery version "1.0"
(: etc 🙂
请注意BNF是如何说明版本声明必须永远跟在模块声明后面的。
命名空间
为了理解XQuery的其他组件,首先理解XML的命名空间非常重要,其中包括多数人忽略的一些细节。传统上,XML命名空间允许具有相同本地名(例如 <table>)的元素具有不同的语法含义。命名空间由URI指定,如http://www.w3.org/1999/xhtml指定一个HTML表,http://furnitureworld.com指定一个四条腿的桌子。虽然它们看起来像HTTP URL,但命名空间只是一些含糊的名字。它们与HTTP协议完全无关,而且虽然在Web上的那个URL可能会有一些内容,但并不是一定要有内容!命名空间由前缀"http://"打头只是由W3C开始的,已经成为一个惯例,事实上可以使用任何前缀。
的确,这很令人费解。如果命名空间不是根据URI标准化,如ns:org.w3.1999.xhtml,也许会更容易理解一些。
XML元素和属性总是与命名空间相关联,即使只是一个不存在的命名空间。特殊的xmlns属性声明一个命名空间前缀别名,用来将元素放到命名空间中。别名在声明xmlns属性的元素的范围内(可用),所有属性和元素都保留在这个元素中。在这个范围内,任何使用该前缀的元素或属性都被认为是在与该别名相关联的命名空间中。例如,下面的代码在命名空间中有三个节点:
<xhtml:table
xmlns_xhtml=
"http://www.w3.org/1999/xhtml"
xmlns_xlink=
"http://www.w3.org/1999/xlink">
<xhtml:tr width="200" xlink_href="#x">
<xhtml:td/>
</xhtml:tr>
</xhtml:table>
表元素声明了两个命名空间别名:xhtml和xlink,它们与两个不同的标准URI相关联。<table>、<tr>和<td> 元素被放在命名空间http://www.w3.org/1999/xhtml中,而href属性被放在命名空间http://www.w3.org/1999/xlin
k中。width属性在前面提到的不存在的命名空间(即第三个命名空间)中。请记住,命名空间的重要组成部分是URI,而不是前缀别名,如果元素的URI相匹配,则它们被认为是在同一个命名空间中,尽管它们的前缀别名可能不同。
"默认的命名空间"是不带前缀的元素使用的命名空间。它由不包括前缀的特殊xmlns属性来分配。默认的命名空间在元素的范围内有效,并请求该元素及其内容,而且会对这个范围内的所有无前缀的元素起作用。但是,默认的命名空间对无前缀的属性不起作用。来看下面这个例子:
<table
xmlns="http://www.w3.org/1999/xhtml"
xmlns_xlink=
"http://www.w3.org/1999/xlink">
<tr width="200" xlink_href="#x">
<td/>
</tr>
</table>
这个XML在语法上等同于前面所示的XML。原来别名为"xhtml"的命名空间现在变成了默认的命名空间,并应用到<table>元素内所有无前缀的元素。因此<table>、<tr>和<td>元素仍在命名空间http://www.w3.org/1999/xhtml中。href属性仍在命名空间http://www.w3.org/1999/xlink中。而且由于无前缀的属性不在默认的命名空间中,所以width属性仍在不存在的命名空间中。
缺少的命名空间可以用空的字符串URI来表示。这样就可以直接放到不存在的命名空间中了。例如:
<table xmlns="http://www.w3.org/1999/xhtml">
<tr> <td/> </tr>
<data xmlns=""> <subdata/> </data>
</table>
这里的<table>、<tr>和<td>元素存放在默认的命名空间即http://www.w3.org/1999/xhtml中,而数据和子数据元素则存放在不存在的命名空间中。
在XQuery中,编写XML代码时你可以使用标准的命名空间声明和规则:
let $bug :=
<x:bug xmlns_x="http://www.bug.com/ns">
<x:desc>Order entry fails</x:desc>
</x:bug>
或者,你可以在序中声明一个命名空间别名:
declare namespace x =
"http://www.bug.com/ns"
let $bug :=
<x:bug>
<x:desc>Order entry fails</x:desc>
</x:bug>
你还可以在序中声明一个默认的元素命名空间:
default element namespace =
"http://www.bug.com/ns"
declare namespace xhtml =
"http://www.w3.org/1999/xhtml"
let $bug :=
<bug>
<desc>Order entry fails</desc>
</bug>
let $report :=
<xhtml:span>{$bug}</xhtml:span>
当使用默认的元素命名空间时,无论是用xmlns属性还是用声明语句声明,你都必须小心。路径表达式会受到它们所处位置范围内的命名空间的影响。在XQuery中,输入和输出命名空间没有区别。要理解为什么会发生这种情况,请先设法确定下面这条查询是否运行:
let $bug :=
<bug xmlns="http://www.bug.com/ns">
<desc>Order entry fails</desc>
<cause>{ input()/bugdb
//item[@id="123"] }
</cause>
</bug>
为了让它运行, 和 元素必须放在哪个命名空间中?id属性呢?答案是,由于路径表达式/bugdb//item[@id="123"]出现在默认元素命名空间http://www.bug.com/ns的范围内,所以在这个命名空间中执行无前缀路径表达式组件。这可能正确,也可能不正确。为了使这个查询能够运行,bugdb和item必须在http://www.bug.com/ns命名空间中。id属性不受默认元素命名空间的影响,因为它是一个属性。前面的查询跟下面这个查询一样:
let $bug :=
<x:bug xmlns_x="http://www.bug.com/ns">
<x:desc>Order entry fails</x:desc>
<x:cause>{ input()/x:bugdb
//x:item[@id="123"] }
</x:cause>
</x:bug>
在生成XHTML内容时经常出现这个问题。Microsoft Internet Explorer浏览器不理解带前缀的XHTML,而是使用具有默认元素命名空间的无前缀XHTML来影响所有在XHTML内容中出现的查询。避免这个问题的一种方法就是在默认元素范围外写一些函数,如下所示:
define function get-body() as element() {
doc("test.xml")/body
}
<html xmlns="http://www.w3.org/1999/xhtml">
<head/>
{ get-body() }
</html>
另一种方法是为不存在的命名空间显式地声明一个前缀,如下所示:
declare namespace e = ""
<html xmlns="http://www.w3.org/1999/xhtml">
<head/>
{ doc("test.xml")/e:body }
</html>
函数命名空间
XQuery预定义了5个命名空间前缀:
xml
http://www.w3.org/XML/1998/namespace
xs
http://www.w3.org/2001/XMLSchema
xsi
http://www.w3.org/2001/XMLSchema-instance
fn
http://www.w3.org/2003/05/xpath-functions
xdt
http://www.w3.org/2003/05/xpath-datatypes
XML(xml)、XML模式(xs)和XML模式实例(xsi)是XML命名空间和XML模式标准。5月份的XQuery草案引入了最后两个标准–XPath函数(fn)和XPath数据类型(xdt)。
默认函数命名空间是http://www.w3.org/2003/05/XPath-functions,使用标准的"fn"默认前缀。你可以更改这个命名空间,但是你必须使用内置的"fn"前缀来限定任何内置的函数调用。例如:
default function namespace =
"http://example.com/functions"
fn:string-length("foo")
导入模块将函数和变量(也只能是函数和变量)从外部库模块加载到主模块中。在进行模块导入时,你应该指定一个目标命名空间,它需要与模块中声明的命名空间相匹配。这类似于导入一个Java包,其中导入语句路径必须与包语句的路径相匹配。在导入过程中,你还可以指定加载模块的位置,这个位置以URI的形式给出。任何函数或变量名的冲突都会抛出一个错误。下面是common.xq库模块文件以及随后的main.xq主模块文件:
(: common.xq file 🙂
module "http://www.w3.org/2003/05/
xpath-functions"
define function uris() as xs:string* {
for $n in input()
return xs:string(document-uri($n))
}
(: no body allowed in library modules 🙂
(: main.xq file 🙂
import module
"http://www.w3.org/2003/05/
xpath-functions" at "common.xq"
uris()
common.xq文件(XQuery文件的标准扩展名还没有确定)包含前面给出的uris()函数的定义。利用这个声明,该文件把它的函数放在了http://www.w3.org/ 2003/ 05/XPath-functions命名空间中。main.xq文件用同样的命名空间导入模块。由于uris()函数存放在默认的函数命名空间中,所以main.xq可以调用不带前缀的uris()。
在默认的命名空间中写函数非常方便,而且意味着你不必给你的调用加前缀。但是,high-profile模块应该使用另一个命名空间以避免冲突。我建议像看待Java默认包那样看待"fn"命名空间:它对构造原型很有用,但对已完成的库模块却没什么用途。要想加载带有另一个前缀的模块,可以这样做:
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
更新XQuery(三)
XQuery有了一些新特性,内容包括从原子化到跟踪文件结构。
-
更新XQuery(一)
XQuery有了一些新特性,内容包括从原子化到跟踪文件结构。