DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 將XSLT查找表打包成EXSLT函數
將XSLT查找表打包成EXSLT函數
編輯:XML詳解     

在“XSLT lookup tables”和“技巧:XSLT 查找表中的默認值和錯誤處理”這兩篇技巧中,我說明了如何創建 XSLT 代碼來查找為處理程序提供的靜態值,其中包括默認值和錯誤處理的支持。處理這類查找的代碼非常簡單但是相當笨拙,尤其是在轉換中使用多個表的時候。清單 1 是查找表的示例實現(摘自最近的那篇技巧),在查找失敗時,它可以提供默認值。

  清單 1. 帶有默認值的查找表示例(states-lookup-default.xslt)

<?XML version="1.0"?>
<xsl:transform
 XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
 XMLns:s="http://example.com/states.data"
 version="1.0"
>
 <xsl:output method="text"/>
 <xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
 <xsl:variable name="states-top" select="document('')/*/s:states"/>
 <xsl:template match="label">
  <xsl:value-of select="name"/>
  <xsl:text> of </xsl:text>
  <xsl:apply-templates select="$states-top">
   <xsl:with-param name="curr-label" select="."/>
  </xsl:apply-templates>
 </xsl:template>
 <xsl:template match="s:states">
  <!-- This template updated to add a default value signal -->
  <xsl:param name="curr-label"/>
  <xsl:variable name="look-for" select="$curr-label/address/state"/>
  <xsl:variable name="default" select="s:default"/>
  <xsl:variable name="result"
   select="key('state-lookup', $look-for)/s:name"/>
  <xsl:choose>
   <xsl:when test="$result">
    <xsl:value-of select="$result"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:value-of select="$default"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
 <s:states>
  <s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
  <s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
  <s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
  <s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
  <!-- Added default value -->
  <s:default><s:name>[UNKNOWN]</s:name></s:default>
 </s:states>
</xsl:transform>

 在 XSLT 1.0 中進行模塊化的局限性

  您可能希望將鍵查找代碼打包在指定模板中,使代碼更加清晰。清單 2 就是這樣做的,這種方法不是很好,但在沒有其他復雜因素(或者沒有查找類型的限制)的情況下,這已經是 XSLT 1.0 所能做到的最好的表現了。

  清單 2. 將查找代碼打包成可重用的模板

<?XML version="1.0"?>
<xsl:transform
 XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
 XMLns:s="http://example.com/states.data"
 version="1.0"
>
 <xsl:output method="text"/>
 <xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
 <xsl:variable name="states-top" select="document('')/*/s:states"/>
 <xsl:template match="label">
  <xsl:value-of select="name"/>
  <xsl:text> of </xsl:text>
  <xsl:call-template name="lookup">
   <xsl:with-param name="key-name" select="'state-lookup'"/>
   <xsl:with-param name="look-for" select="address/state"/>
   <xsl:with-param name="default" select="$states-top/s:default"/>
   <xsl:with-param name="table-doc" select="$states-top"/>
  </xsl:call-template>
 </xsl:template>
 <xsl:template name="lookup">
  <xsl:param name="key-name"/> <!-- name of XSLT key -->
  <xsl:param name="look-for"/> <!-- what to look up -->
  <xsl:param name="default"/>
  <!-- Node set whose first item can be used to set the proper context
     for key lookup. -->
  <xsl:param name="table-doc"/>
  <!-- Force context to document where the lookup table is defined -->
  <xsl:for-each select='$table-doc[1]'>
   <xsl:variable name="result" select="key($key-name, $look-for)"/>
   <xsl:choose>
    <xsl:when test="$result">
     <xsl:value-of select="$result"/>
    </xsl:when>
    <xsl:otherwise>
     <xsl:value-of select="$default"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:for-each>
 </xsl:template>
 <s:states>
  <s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
  <s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
  <s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
  <s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
  <!-- Added default value -->
  <s:default><s:name>[UNKNOWN]</s:name></s:default>
 </s:states>
</xsl:transform>


 

清單 2 的主要問題是,查找總是通過 $result 的字符串值在 lookup 模板中呈現。但這不是我所期望的。如果標簽是“CO”,我希望顯示“Colorado”。在清單 2 中,$result 的值是“COColorado”,因為查找的結果是整個匹配的 s:state 元素。與清單 1 相比,在清單 1 中,通過選擇 s:name 子元素,就可以立即將查找結果限制為目標字符串。

  不同的查找情況需要不同的 XPath 表達式,從 XSLT key 返回的節點中提取特定的結果。為了解決上述問題,您可能希望以某種方式在 lookup 模板中傳遞用於 XSLT key 結果的 XPath 表達式。但是這樣做需要能夠動態指定 XPath,而 XSLT 1.0 沒有提供這種能力。或者,您也許認為可以將 xsl:call-template 放在一個 xsl:variable 中,然後從該變量獲得需要的 s:name 子元素。但這樣做也不行,因為創建的變量將計算得到一個結果樹片段(RTF),而 XSLT 1.0 不允許對 RTF 執行軸操作(axis Operation)。

  清單 2 還提出了其他一些不那麼顯著的問題。整個 xsl:call-template 結構笨拙而冗長。XSLT key 的行為有一個容易造成混亂的要求,即使用 xsl:for-each 將上下文變成查找表文檔中的一個節點,這個文檔通常仍然與源文檔(該例中是樣式表文檔本身)有所不同。

  讓 EXSLT 來幫忙

  除了最後一個問題之外,使用 EXSLT 可以很好地解決其他所有問題。清單 3 中的代碼完全模仿了清單 1 的行為,並使用兩個 EXSLT 函數解決了上述的問題。它還允許進行整潔的打包。

  清單 3. 清單 1 的正確模塊化,使用 EXSLT

<?XML version="1.0"?>
<xsl:transform version="1.0"
 XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
 XMLns:s="http://example.com/states.data"
 XMLns:func="http://exslt.org/functions"
 XMLns:dyn="http://exslt.org/dynamic"
 extension-element-prefixes="func"
>
 <xsl:output method="text"/>
 <xsl:key name="state-lookup" match="s:state" use="s:abbr"/>
 <xsl:variable name="states-top" select="document('')/*/s:states"/>
 <xsl:template match="label">
  <xsl:value-of select="name"/>
  <xsl:text> of </xsl:text>
  <xsl:value-of
   select="s:lookup('state-lookup', address/state,
            $states-top/s:default, $states-top,
            '$result/s:name')"/>
 </xsl:template>
 <func:function name="s:lookup">
  <xsl:param name="key-name"/> <!-- name of XSLT key -->
  <xsl:param name="look-for"/> <!-- what to look up -->
  <xsl:param name="default"/>
  <!-- Node set whose first item can be used to set the proper context
     for key lookup. -->
  <xsl:param name="table-doc" select="$default"/>
  <!-- A string containing an XPath expression to be evaluated to
     get the final result. By default, just render the XSLT key
     result as is -->
  <xsl:param name="result-expr" select="'$result'"/>
  <!-- Force context to document where the lookup table is defined -->
  <xsl:for-each select='$table-doc[1]'>
   <xsl:variable name="result" select="key($key-name, $look-for)"/>
   <xsl:choose>
    <xsl:when test="$result">
     <func:result select="dyn:evaluate($result-expr)"/>
    </xsl:when>
    <xsl:otherwise>
     <func:result select="$default"/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:for-each>
 </func:function>
 <s:states>
  <s:state><s:abbr>CO</s:abbr><s:name>Colorado</s:name></s:state>
  <s:state><s:abbr>CT</s:abbr><s:name>Connecticut</s:name></s:state>
  <s:state><s:abbr>ID</s:abbr><s:name>Idaho</s:name></s:state>
  <s:state><s:abbr>NJ</s:abbr><s:name>New Jersey</s:name></s:state>
  <!-- Added default value -->
  <s:default><s:name>[UNKNOWN]</s:name></s:default>
 </s:states>
</xsl:transform>

  與期望的相同,用戶定義函數 s:lookup 可以在多數查找中重用。可以從第一個模板中看到更簡潔 XPath 語法是如何簡化查找代碼。函數調用中傳遞的參數順序與函數定義中 xsl:param 的順序對應。func:result 擴展用於確定回傳給調用者的值。在其他方面,用戶定義函數體的行為類似於 XSLT 1.0 模板。這種技術提供了很大的靈活性。下面的片段是調用函數的另一種方法,它也能很好地工作:

  <xsl:value-of
   select="s:lookup('state-lookup', address/state,
            $states-top/s:default, $states-top')/s:name"/>

  用戶定義函數可以巧妙地繞開 XSLT 的 RTF 限制。

  結束語

  您可以找到很多克服 XSLT 1.0 缺點和局限性的 EXSLT 擴展。這篇技巧介紹了如何使用 EXSLT 簡化已經非常棒的 XSLT 1.0 技術。


 


XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved