2009-11-16 109 views
2

我用XSLT 1.0對字符串進行標記,並試圖防止空字符串被識別爲標記。這裏是全功能的基礎上,XSLT Cookbook當測試掛在無限循環中

<xsl:template name="tokenize"> 
    <xsl:param name="string" select="''" /> 
    <xsl:param name="delimiters" select="';#'" /> 
    <xsl:param name="tokensplitter" select="','" /> 
    <xsl:choose> 
     <!-- Nothing to do if empty string --> 
     <xsl:when test="not($string)" /> 

     <!-- No delimiters signals character level tokenization --> 
     <xsl:when test="not($delimiters)"> 
      <xsl:call-template name="_tokenize-characters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="delimiters" select="$delimiters" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

<xsl:template name="_tokenize-characters"> 
    <xsl:param name="string" /> 
    <xsl:param name="tokensplitter" /> 
    <xsl:if test="$string"> 
     <token><xsl:value-of select="substring($string, 1, 1)"/></token> 
     <xsl:call-template name="_tokenize-characters"> 
      <xsl:with-param name="string" select="substring($string, 2)" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

<xsl:template name="_tokenize-delimiters"> 
    <xsl:param name="string" /> 
    <xsl:param name="delimiters" /> 
    <xsl:param name="tokensplitter" /> 

    <!-- Extract a delimiter --> 
    <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)"/> 
    <xsl:choose> 
     <!-- If the delimiter is empty we have a token --> 
     <xsl:when test="not($delimiter) and $string != ''"> 
      <xsl:text>£</xsl:text> 
      <token><xsl:value-of select="$string"/></token> 
      <xsl:text>$</xsl:text> 
      <xsl:value-of select="$tokensplitter"/> 
     </xsl:when> 
     <!-- If the string contains at least one delimiter we must split it --> 
     <xsl:when test="contains($string, $delimiter)"> 
      <!-- If it starts with the delimiter we don't need to handle the before part --> 
      <xsl:if test="not(starts-with($string, $delimiter))"> 
       <!-- Handle the part that comes before the current delimiter with the next delimiter. --> 
       <!-- If there is no next the first test in this template will detect the token. --> 
       <xsl:call-template name="_tokenize-delimiters"> 
        <xsl:with-param name="string" select="substring-before($string, $delimiter)" /> 
        <xsl:with-param name="delimiters" select="substring($delimiters, 2)" /> 
        <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
       </xsl:call-template> 
      </xsl:if> 
      <!-- Handle the part that comes after the delimiter using the current delimiter --> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="substring-after($string, $delimiter)" /> 
       <xsl:with-param name="delimiters" select="$delimiters" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise> 
      <!-- No occurrences of current delimiter so move on to next --> 
      <xsl:call-template name="_tokenize-delimiters"> 
       <xsl:with-param name="string" select="$string" /> 
       <xsl:with-param name="delimiters" select="substring($delimiters, 2)" /> 
       <xsl:with-param name="tokensplitter" select="$tokensplitter" /> 
      </xsl:call-template> 
     </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

string值是我傳遞的是:

歐洲;#6,#Global;#3; #Middle東,非洲 和高加索 ; 2; #Europe;#6; #Global;#3; #Middle 中東,非洲和高加索

(該£$指標只是在那裏,所以我可以看到沒有空字符串輸出。這是在SharePoint內,因此很難調試。)

此代碼掛起XSLT的處理。導致該問題的線路是<xsl:when test="not($delimiter) and $string != ''">。只要我刪除第二個and測試它再次運作。我也試過and string($string)沒有成功。

任何人都知道爲什麼會發生這種情況,以及如何解決它?

回答

4

我相信我的懷疑是正確的:你通過時$string有一個值的<xsl:otherwise>條款下跌,但$delimiter確實不是,如你所說,造成無限循環。

新增以下條款<xsl:when>後的第一個:

<xsl:when test="not($delimiter) and $string = ''" /> 

這將防止當它不應該進入<xsl:otherwise>塊的執行。


這是怎麼回事,爲什麼它是循環的一個更詳盡的解釋:

有在<xsl:choose>塊三個分支。

<xsl:when test="not($delimiter) and $string != ''"> 
    <xsl:when test="contains($string, $delimiter)"> 
    <xsl:otherwise> 

所以,當既不$string也不$delimiter包含值,所述第一條件失敗(因爲$string != ''爲假)。第二個條件通過(因爲contains(nil,nil)總是返回true(在Visual Studio中確認)),它使用相同的參數再次調用模板(因爲substring-before由於不包含空分隔符而返回空字符串)。人類,一個無限循環。

修復的方法是添加一個新的,空的條件:

<xsl:when test="not($delimiter) and $string != ''"> 
    <xsl:when test="not($delimiter) and $string = ''" /> 
    <xsl:when test="contains($string, $delimiter)"> 
    <xsl:otherwise> 

編輯:我周圍戳,我無法找到所定義的contains當第二個參數行爲參考是空的或零。測試表明,當第二個參數爲空或零時,Microsoft Visual Studio的XSLT引擎返回true。我不確定這是否是已定義的行爲,或者是否由實現者決定。有沒有人有這個決定性的答案? Tomalak,我看着你。

+0

謝謝,我可以在這裏看到錯誤。 – 2009-11-16 17:14:22

+0

@Alex Angas:沒問題。只要確保永遠不要只關注「if/elseif」塊的一個部分(或者您使用的任何語言的等價物)。調試其中一部分時需要考慮整個模塊。 – Welbog 2009-11-16 17:17:50

+0

+1對於一個很好的說明/解釋。是的,我應該更清楚。 – 2009-11-16 17:27:05

0

是不是string保留字?你能否嘗試替換其他名稱?

編輯:提供的代碼運行,沒有問題就在這裏:XSLT Tryit Editor v1.0使用:

<xsl:call-template name="tokenize"> 
    <xsl:with-param name="string">Europe;#6;#Global...</xsl:with-param> 
</xsl:call-template> 
+0

這是從XSLT食譜直接,一直工作到現在......他們會使用它作爲變量名似乎很奇怪。 – 2009-11-16 16:46:10

+0

您可以發表更多的代碼,或簡單的測試用例嗎?我不知道那本食譜 – 2009-11-16 16:47:58

+0

用完整的代碼和例子更新了這個問題。 – 2009-11-16 16:58:11