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 East, Africa and Caucasus;2;#Europe;#6;#Global;#3;#Middle East, Africa and Caucasus

£$指示符就在那里,所以我看不到空字符串输出。这是在 SharePoint 中,因此很难调试。)

此代码挂起 XSLT 的处理。导致问题的行是<xsl:when test="not($delimiter) and $string != ''">. 一旦我删除第二个and测试,它就会再次起作用。我也试过and string($string)没有成功。

任何人都知道为什么会发生这种情况以及如何解决它?

4

2 回答 2

4

我相信我的怀疑是正确的:正如你所说,当你有一个价值但没有价值时,你会陷入你的<xsl:otherwise>条款,从而导致无限循环。$string$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对第二个参数为空或 nil 时的定义行为的引用。测试表明,Microsoft Visual Studio 的 XSLT 引擎true在第二个参数为空或 nil 时返回。我不确定这是否是已定义的行为,或者是否由实施者决定。有人对此有明确的答案吗?托马拉克,我在看着你。

于 2009-11-16T17:09:01.173 回答
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>
于 2009-11-16T16:44:22.143 回答