跳轉到內容

XQuery/轉換習語

來自 Wikibooks,開放書籍,開放世界

使用基本 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>
};
華夏公益教科書