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>
};