XSLT节点集分词

3

我正在尝试创建一个变量,用于存储以init cap形式输入的字符串(TypeInput)的值。这个新变量将在我的样式表中的不同位置使用。我创建了一个模板,调用它来将输入字符串转换为init cap形式。然而,当我运行样式表时,结果变量TypeInputInitCap在调试器中显示为NodeSet(1),并且在输出中没有输出文本。有任何想法吗?请参见以下示例。

<xsl:variable name="TypeInputInitCap">
    <xsl:call-template name="ConvertToInitCapString">
        <xsl:with-param name="str" select="$TypeInput"></xsl:with-param>
    </xsl:call-template>    
</xsl:variable>

<xsl:template name="ConvertToInitCapString">
    <xsl:param name="str"></xsl:param>        
    <!-- Extract each component of the name delimited by . -->
    <xsl:variable name="TokenNodeSet">
        <xsl:for-each select="tokenize($str, '.')">
            <!-- Init cap each component -->
            <xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>
        </xsl:for-each>
    </xsl:variable>
    <xsl:for-each select="$TokenNodeSet">
        <xsl:value-of select="."></xsl:value-of>
        <xsl:if test="not(last())">
            <xsl:text>.</xsl:text>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

1
请给出一个输入的示例(str参数的值)的例子。 - LarsH
3个回答

1

我认为问题在于$TokenNodeSet变量仅包含单个字符串,因此第二个for-each仅循环一次。

尝试这样做怎么样:

<xsl:template name="ConvertToInitCapString"> 
  <xsl:param name="str"></xsl:param>         
  <xsl:for-each select="tokenize($str, '\.')"> 
    <xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"/> 
    <xsl:if test="not(last())"> 
        <xsl:text>.</xsl:text> 
    </xsl:if> 
  </xsl:for-each> 

编辑

根据LarsH在评论中的建议,已修复上面的tokenize()调用。


没错...for-each会连接它的结果:"对于输入序列中的每个项目,评估序列构造器会产生一系列项目...这些输出序列被连接"(http://www.w3.org/TR/xslt20/#element-for-each)。规范有点模糊,但字符串结果显然会被连接成一个单独的字符串,而不是一系列字符串。**然而**,这并不能解释OP说他没有得到任何输出的事实。他应该得到整个字符串(没有`.`字符)作为输出。 - LarsH
@LarsH:是的,要创建一个字符串序列,您需要创建一个封装每个字符串的XML元素。 - MiMo
1
@OP:另一个主要问题是使用 '.' 作为您的分词分隔符模式。第二个参数是正则表达式,因此 '.' 匹配任何字符。如果您将其更改为 '\.',MiMo 的解决方案应该可以工作。 - LarsH
可能存在一系列字符串,这些字符串未包装在XML元素中,例如tokenize()函数返回的结果。但也许你特别指的是xsl:for-each的结果。 - LarsH
是的,我的问题确实是我没有转义“.”字符。在转义后,现在我得到了一个带有“.”的连接字符串。 - wavelength

0

我会替换

<xsl:variable name="TokenNodeSet">
    <xsl:for-each select="tokenize($str, '.')">
        <!-- Init cap each component -->
        <xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>
    </xsl:for-each>
</xsl:variable>

使用

<xsl:variable name="TokenNodeSet" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />

或者更好的是用

<xsl:variable name="TokenNodeSet" as="xs:string*" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />

或者最后,如果是XSLT 2.0,因为没有节点集,我会将变量重命名为例如

<xsl:variable name="TokenSequence" as="xs:string*" select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" />

这是一个优雅而简洁的解决方案,将一切放在一行中。 - wavelength

0
感谢大家的帮助。我的模板中第二部分是不必要的,所以现在我正在使用这个版本,它可以重新在标记之间添加一个"."字符(我没有使用本主题建议的简短版本,因为如果连接的话最后会多出一个句点)。
<xsl:template name="ConvertToInitCapString">
    <xsl:param name="str" select="."></xsl:param>        
    <!-- Extract each component of the name delimited by . -->
    <xsl:for-each select="tokenize($str, '\.')">
        <!-- Init cap each component -->
        <xsl:value-of select="concat(upper-case(substring(.,1,1)), lower-case(substring(.,2)))"></xsl:value-of>                
        <xsl:if test="position() != last()">
            <xsl:value-of select="'.'"></xsl:value-of>
        </xsl:if>
    </xsl:for-each>                              
</xsl:template>

请注意,使用XSLT 2.0,您可以简单地使用我的答案中的变量<xsl:value-of select="$TokenSequence" separator="."/><xsl:value-of select="string-join($TokenSequence, '.')"/>。或者您可以使用<xsl:value-of select="for $token in tokenize($str, '\.') return concat(upper-case(substring($token,1,1)), lower-case(substring($token,2)))" separator="."/>来避免使用变量。 - Martin Honnen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接