2009-11-16 71 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