我在 上一篇技巧中指出, 在通過編程實現效率非常低或者無法實現的情況下,查找表是一種常用的快速搜索技術。查找表由聯系鍵和值的映射組成。 那篇文章展示了如何用 XSLT 構造查找表,但沒有涉及以下兩個技術細節:如何處理查找表中沒有發現的鍵,或者如何提供默認值。閱讀本文之前,您可以先看一看那篇文章。
查找錯誤
上一篇文章中,源代碼清單中的州縮寫名恰好都在查找表中。但是如果使用偽造的州縮寫名來調用轉換,又會怎麼樣呢,比如 ZZ。下面的片段取自上一篇文章中的實際查找代碼:
<xsl:value-of select="key('state-lookup', $curr-label/address/state)/s:name"/>
如果 $curr-label 是 ZZ,就無法在 state-lookup 鍵中找到值, key 函數調用的結果是一個空節點集, xsl:value-of 指令將其轉化為一個空字符串。對於您來說,也許這其中就有足夠多的錯誤,但也可能不是錯誤。比如,如果從查找表某個值得到空字符串是合法的,就無法和錯誤區分開。報告查找錯誤最簡單(而且不會出錯)的辦法是檢查 key 函數返回的節點集是否為空,然後生成一個 xsl:message 元素並賦予屬性 terminate="yes"。清單 1 是一個完整的例子,它以上一篇文章中的樣式表內查找技術為基礎,增加了錯誤通知技術。
清單 1. 遇到查找錯誤停止的查找表(states-lookup-error.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 lookup error signal -->
<xsl:param name="curr-label"/>
<xsl:variable name="look-for" select="$curr-label/address/state"/>
<xsl:variable name="result"
select="key('state-lookup', $look-for)/s:name"/>
<xsl:if test="not($result)">
<xsl:message terminate="yes">
Lookup error on key: <xsl:value-of select="$look-for"/>
</xsl:message>
</xsl:if>
<!-- Push the string value of result to the output stream -->
<xsl:value-of select="$result"/>
</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>
</s:states>
</xsl:transform>
上一篇技巧中介紹了這些代碼的運作過程。惟一發生變化的是模板 match="s:states",增加了前面所說的錯誤通知技術。其原理是 XSLT key 永遠不會返回空節點集,除非沒有找到查找值。清單 2 是一個郵件標簽文件,其中一項使用了查找表中沒有的州縮寫名。
清單 2. 郵件標簽源文件
<?XML version="1.0"?>
<labels>
<label>
<name>Ezra Pound</name>
<address>
<street>45 Usura Place</street>
<city>Hailey</city>
<state>ZZ</state>
</address>
</label>
<label>
<name>William Williams</name>
<address>
<street>100 Wheelbarrow Blvd</street>
<city>Patterson</city>
<state>NJ</state>
</address>
</label>
</labels>
不同的 XSLT 處理程序以不同的方式報告 <xsl:message terminate="yes"> 消息,下面是 4XSLT 處理程序的輸出結果。
$ 4xslt listing2.XML states-lookup-error.xslt
Ezra Pound of In stylesheet
file://dW/articles/xslt-lookup-defaults/states-lookup-error.xslt,
line 28, column 6:
A message instruction in the Stylesheet requested termination of processing:
Lookup error on key: ZZ
默認值
如果希望在查找失敗的時候不報告錯誤,還可以設置一個默認值。使用的技術和錯誤通知類似,但不是發送停止消息,而是提供默認值。也可以將停止消息編碼到查找表中。清單 3 中的例子為查找表中找不到的所有鍵提供了值 [UNKNOWN]。
清單 3. 帶有默認值的查找表(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>
這裡仍然要檢查 key 函數的結果是否為空,但這裡提供了一個默認值。這個值在查找表的一個特殊元素( s:states/s:default)中給出。
下面是 4XSLT 處理程序的輸出結果:
$ 4xslt listing2.XML states-lookup-default.xslt
Ezra Pound of [UNKNOWN]
William Williams of New Jersey
結束語
這些細化措施可以使 XSLT 查找表更方便。您現在看到在確定這類代碼的行為時有很大的靈活性,也許還希望有一種更好的方法將查找表打包以供重用。在基本的 XSLT 1.0 中不太容易做到這一點,但是將來考察 EXSLT(請參閱 參考資料)中的某些設施時,我還會探討這個話題,那些工具可以使查找表更便於使用。