XQuery/转换习语
使用基本 typeswitch 语句的文档转换将相同的转换应用于元素,而不管它在文档中的位置如何。转换还保留文档顺序,因为它按文档顺序处理元素。与 XSLT 相比,XQuery 缺少一些机制,如模式、优先级和编号。本文探讨了这些限制中的一些。
该示例使用自定义 XML 架构来标记书籍“搜索:图形网络指南”的内容,作者 Ken Coupland,网站汇编。此文档使用特定于站点的架构进行格式化。该文档包含标记有类别的站点元素,以及提供类别注释的类别元素。为了比较,此数据集用于一个学生案例研究,该案例研究使用 XSLT 进行转换。
module namespace coupland = "http://www.cems.uwe.ac.uk/xmlwiki/coupland";
(: conversion module generated from a set of tags
:)
declare function coupland:convert($nodes as node()*) as item()* {
for $node in $nodes
return
typeswitch ($node)
case element(websites) return coupland:websites($node)
case element(sites) return coupland:sites($node)
case element(site) return coupland:site($node)
case element(uri) return coupland:uri($node)
case element(name) return coupland:name($node)
case element(description) return coupland:description($node)
default return
coupland:convert-default($node)
};
declare function coupland:convert-default($node as node()) as item()* {
$node
};
declare function coupland:websites($node as element(websites)) as item()* {
element websites{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:sites($node as element(sites)) as item()* {
element sites{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:site($node as element(site)) as item()* {
element site{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:uri($node as element(uri)) as item()* {
element uri{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:name($node as element(name)) as item()* {
element name{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:description($node as element(description)) as item()* {
element description{
$node/@*,
coupland:convert($node/node())
}
};
模块代码只是一个基本骨架,我们会对其进行编辑以定制转换。在这个示例中,我们将文档转换为 HTML。这将需要编辑许多元素转换器。
更改 convert-default
函数以提供不同的默认操作。例如
declare function coupland:convert-default ($node) {
if ($node instance of element())
then coupland:convert($node/node())
else $node
};
将包含节点的内容,但删除标签及其属性。
站点描述将被呈现为 divs
declare function coupland:description($node as element(description)) as item()* {
element div{
$node/@*,
coupland:convert($node/node())
}
};
'class' 元素不需要
declare function coupland:class($node as element(class)) as item()* {
()
};
图像元素应使用 uri 作为源转换为 html img
元素
declare function coupland:image($node as element(image)) as item()* {
element div {
element img {
attribute src { $node}
}
}
};
默认情况下,文档中任何位置具有相同名称的所有元素都以相同的方式转换。这通常不是必需的
declare function coupland:name($node as element(name)) as item()* {
if ($node/parent::node() instance of element(site))
then
element h3{
$node/@*,
coupland:convert($node/node())
}
else
element h1{
$node/@*,
coupland:convert($node/node())
}
};
每个站点都将按名称、uri 然后其余子元素的顺序呈现
declare function coupland:site($node as element(site)) as item()* {
element div{
element div {
coupland:convert($node/name),
coupland:convert($node/uri)
} ,
coupland:convert($node/(node() except (uri,name)))
}
};
xsl:number 指令提供了一种生成分层节号的机制。此指令非常强大。在特定情况下,我们可以使用函数生成数字。
例如,要对类别进行编号,我们可以使用此函数为一系列兄弟节点中的节点创建编号。请注意,该编号基于原始文档中的节点顺序,而不是转换后的文档(与 xsl:number 相同)。
declare function coupland:number($node) as xs:string {
concat(count($node/preceding-sibling::node()[name(.) = name($node)]) + 1,". ")
};
并在转换类别名称时调用此函数
element h2{
$node/@*,
coupland:number($node/parent::node()),
coupland:convert($node/node())
转换可以清楚地应用于不同的文档,但通常相同的转换要在不同的上下文中使用。XSLT 提供了对所有模板全局的参数和变量。
在 XQuery 中,我们可以在模块中声明全局变量,也可以在函数周围传递一个或多个参数(模块生成 在这里很有用)。
....
XSLT 使用模式机制来允许以多种方式处理相同的模板。一个常见的用例是,相同的转换必须生成索引和内容。
有几种方法可以实现。我们可以通过在调用中传递额外的模式参数来模仿 XSLT 方法,并在每个函数中选择要应用的转换。或者,我们将模式追加到函数名称。使用上下文(全局或传递)更难,因为需要更新模式。
最简单的方法是使用两个 typeswitch 转换,并在更高层级组合结果。这清楚地分离了两种转换模式。模块生成 技术在这里很有用。
整个 HTML 文档可以在根元素的转换器中构建。该页面使用蓝图样式表。每个站点的类别都会被渲染,包括分类到该类别的站点。
declare function coupland:websites($node as element(websites)) as item()* {
(: the root element so convert to html :)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Web Sites by Coupland</title>
<link rel="stylesheet" href="../../css/blueprint/screen.css" type="text/css" media="screen, projection"/>
<link rel="stylesheet" href="../../css/blueprint/print.css" type="text/css" media="print"/>
<!--[if IE ]><link rel="stylesheet" href="../../css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]-->
<link rel="stylesheet" href="screen.css" type="text/css" media="screen"/>
</head>
<body>
<div class="container">
{
for $category in $node/category
order by $category/class
return
<div>
<div class="span-10">
{coupland:convert($category)}
</div>
<div class="span-14 last">
{for $site in $node/sites/site[category=$category/class]
order by ($site/sortkey,$site/name)[1]
return
coupland:convert($site)
}
</div>
<hr />
</div>
}
</div>
</body>
</html>
};
完整的 XQuery 模块现在看起来像这样
module namespace coupland = "http://www.cems.uwe.ac.uk/xmlwiki/coupland";
(: conversion module generated from a set of tags
:)
declare function coupland:convert($nodes as node()* as node()?) as item()* {
for $node in $nodes
return
typeswitch ($node)
case element(category) return coupland:category($node)
case element(class) return coupland:class($node)
case element(description) return coupland:description($node)
case element(em) return coupland:em($node)
case element(hub) return coupland:hub($node)
case element(image) return coupland:image($node)
case element(name) return coupland:name($node)
case element(p) return coupland:p($node)
case element(q) return coupland:q($node)
case element(site) return coupland:site($node)
case element(sites) return coupland:sites($node)
case element(sortkey) return coupland:sortkey($node)
case element(subtitle) return coupland:subtitle($node)
case element(uri) return coupland:uri($node)
case element(websites) return coupland:websites($node)
default return
coupland:convert-default($node)
};
declare function coupland:convert-default($node as node() as node()?) as item()* {
$node
};
declare function coupland:category($node as element(category) as node()?) as item()* {
if ($node/parent::node() instance of element(site))
then ()
else
element div{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:class($node as element(class) as node()?) as item()* {
()
};
declare function coupland:description($node as element(description) as node()?) as item()* {
element div{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:em($node as element(em) as node()?) as item()* {
element em{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:hub($node as element(hub) as node()?) as item()* {
element hub{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:image($node as element(image) as node()?) as item()* {
element div {
element img {
attribute src { $node}
}
}
};
declare function coupland:name($node as element(name) as node()?) as item()* {
if ($node/parent::node() instance of element(site))
then
element span {
attribute style {"font-size: 16pt"},
$node/@*,
coupland:convert($node/node())
}
else
element h1{
$node/@*,
coupland:number($node/parent::node()),
coupland:convert($node/node())
}
};
declare function coupland:p($node as element(p) as node()?) as item()* {
element p{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:q($node as element(q) as node()?) as item()* {
element q{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:site($node as element(site) as node()?) as item()* {
element div{
element div {
coupland:convert($node/name),
coupland:convert($node/uri)
} ,
coupland:convert($node/(node() except (uri,name)))
}
};
declare function coupland:sites($node as element(sites) as node()?) as item()* {
for $site in $node/site
order by $node/sortkey
return
coupland:convert($node/site)
};
declare function coupland:sortkey($node as element(sortkey) as node()?) as item()* {
()
};
declare function coupland:subtitle($node as element(subtitle) as node()?) as item()* {
element div{
$node/@*,
coupland:convert($node/node())
}
};
declare function coupland:uri($node as element(uri) as node()?) as item()* {
<span>
{element a{
attribute href {$node },
"Link"
}
}
</span>
};
declare function coupland:websites($node as element(websites) as node()?) as item()* {
(: the rot element so convert to html :)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Web Sites by Coupland</title>
<link rel="stylesheet" href="../../css/blueprint/screen.css" type="text/css" media="screen, projection"/>
<link rel="stylesheet" href="../../css/blueprint/print.css" type="text/css" media="print"/>
<!--[if IE ]><link rel="stylesheet" href="../../css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]-->
<link rel="stylesheet" href="screen.css" type="text/css" media="screen"/>
</head>
<body>
<div class="container">
{
for $category in $node/category
order by $category/class
return
<div>
<div class="span-10">
{coupland:convert($category)}
</div>
<div class="span-14 last">
{for $site in $node/sites/site[category=$category/class]
order by ($site/sortkey,$site/name)[1]
return
coupland:convert($site)
}
</div>
<hr />
</div>
}
</div>
</body>
</html>
};