如何在XSLT中将字符串格式化为帕斯卡命名法?

10

我正在尝试在XSLT中格式化字符串,需要将其格式化为PascalCase以便适用于我正在处理的应用程序。

例如:

this_text 将变成 ThisText
this_long_text 将变成 ThisLongText

是否有可能设置一个输入来格式化字符串,以便我不必多次重新创建格式?


好问题 (+1)。请看我的答案,其中包含完整的XSLT解决方案 :) - Dimitre Novatchev
反转过程也是可用的。请参见下面的答案。;-) - Chris Nava
5个回答

8

这个转换

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vLower" select=
  "'abcdefghijklmnopqrstuvwxyz'"/>

 <xsl:variable name="vUpper" select=
  "'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="text()">
  <xsl:call-template name="Pascalize">
   <xsl:with-param name="pText" select="concat(., '_')"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="Pascalize">
  <xsl:param name="pText"/>

  <xsl:if test="$pText">
   <xsl:value-of select=
    "translate(substring($pText,1,1), $vLower, $vUpper)"/>

   <xsl:value-of select="substring-before(substring($pText,2), '_')"/>

   <xsl:call-template name="Pascalize">
     <xsl:with-param name="pText"
       select="substring-after(substring($pText,2), '_')"/>
   </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

应用于此XML文档时

<t>
  <a>this_text</a>
  <b>this_long_text</b>
</t>

产生所期望的结果:

<t>
    <a>ThisText</a>
    <b>ThisLongText</b>
</t>

顺便提一下,这是驼峰命名法,而这是帕斯卡命名法


谢谢您的快速回复 - 我正在着手实施它。我同意安德鲁的看法 - 做得很好! - OpenDataAlex
快速跟进问题 - 我正在构建的转换器是将XML文件转换为YAML文件。这似乎影响所有文本,而不仅仅是特定的标题。有没有办法指定我想要通过Pascalize模板运行的文本?再次感谢您引导我走上正确的道路。 - OpenDataAlex
@DBA_Alex:您可以将任何字符串(例如包含在xsl:variable中的字符串)作为“Pascalize”模板的“$pText”参数传递。 - Dimitre Novatchev
@Andrew-Hare:谢谢,这是高度的赞扬! - Dimitre Novatchev

6

在此,两年之后,提供一个XSLT 2.0的解决方案:

<xsl:function name="fn:pascal-case">
    <xsl:param name="string"/>
    <xsl:value-of select="string-join(for $s in tokenize($string,'\W+') return concat(upper-case(substring($s,1,1)),substring($s,2)),'')"/>
</xsl:function>

如果遇到非单词字符,它将把“this_long_text”或“this-long-text”转换为“ThisLongText”。

在我最熟悉的正则表达式语言中(perl、pcre等),下划线被认为是“\w”字符类的一部分(因此不是\W的一部分),但对于XSLT 2.0,使用了XSD数据类型(http://www.w3.org/TR/xmlschema-2/),“\w”的定义如下:

[#x0000-#x10FFFF]-[\p{P}\p{Z}\p{C}] (all characters except the set of "punctuation", "separator" and "other" characters)

所以 '\W' 包括下划线。

4
这个版本对我有用。我添加了一个选择,当没有下划线时输出字符串的“剩余部分”。
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

<xsl:template name="Pascalize">
    <xsl:param name="pText" />
    <xsl:if test="$pText">
        <xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)" />
        <xsl:choose>
            <xsl:when test="contains($pText, '_')"> 
                <xsl:value-of select="substring-before(substring($pText,2), '_')" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="substring($pText,2)" />
            </xsl:otherwise>
        </xsl:choose>
        <xsl:call-template name="Pascalize">
            <xsl:with-param name="pText" select="substring-after(substring($pText,2), '_')" />
        </xsl:call-template>
    </xsl:if>
</xsl:template>

此外,如果有人在这里寻找相反的过程(我今天也需要,并且无法找到任何一个示例),请注意...
<xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>

<xsl:template name="TitleCase">
    <xsl:param name="pText" />
    <xsl:call-template name="TitleCase_recurse">
        <xsl:with-param name="pText" select="concat(translate(substring($pText,1,1), $vLower, $vUpper), substring($pText,2))" />
    </xsl:call-template>
</xsl:template>

<xsl:template name="TitleCase_recurse">
    <xsl:param name="pText" />
    <xsl:if test="string-length($pText) &gt; 1">
        <xsl:if test="not(substring($pText,1,1) = ' ' and substring($pText,1,1) = ' ')">
            <xsl:value-of select="substring($pText,1,1)" />
        </xsl:if>
        <xsl:if test="translate(substring($pText,1,1), $vLower, $vUpper) != substring($pText,1,1)">
            <xsl:if test="translate(substring($pText,2,1), $vLower, $vUpper) = substring($pText,2,1)">
                <xsl:text> </xsl:text>
            </xsl:if>
        </xsl:if>
        <xsl:call-template name="TitleCase_recurse">
            <xsl:with-param name="pText" select="substring($pText,2)" />
        </xsl:call-template>
    </xsl:if>
    <xsl:if test="string-length($pText) = 1">
        <xsl:value-of select="$pText" />
    </xsl:if>
</xsl:template>

当我完全放弃意识后,我的潜意识大脑几个小时后会突然冒出一个答案,这时我非常喜欢它。;-)


干得漂亮。这些东西总是一个学习过程,嘿。 - OpenDataAlex

1
我想通过以下 XSLT 函数调用来实现“Pascal化”:
<xsl:value-of select="fn:replace(@name,'_(\w{1})','\U$1')"/>

很不幸,处理器抛出了错误信息“replace()中的无效替换字符串:\字符必须跟随\或$”

问题在于\U修饰符,它应该对匹配模式进行大写转换。如果我将其更改为

<xsl:value-of select="fn:replace(@name,'_(\w{1})','\\U$1')"/>

输出的字符串包含序列“\ U”,因为它现在已经被转义 - 但我不想转义它,我希望它有效; -)。 我进行了测试
<xsl:value-of select="fn:replace(@name,'_(\w{1})','$1')"/>

(不将匹配项转换为大写)这样做很好。但是,它当然不会大写,只是删除下划线并将下划线后面的字母替换为其本身,而不是将其大写。我在这里做错了什么,还是我的XSLT处理器的正则表达式实现不支持\U修饰符?


我并不是正则表达式专家,但我认为不支持\U。 - OpenDataAlex

0
感谢Dimitre的帮助,我已经完成了大部分工作。当我将字符串通过Pascalize模板运行时,最后一个'_'之后的部分被截断了。可能有更简洁的方法,但这是我使用的代码:
<xsl:template name="Pascalize">
    <xsl:param name="pText"/>

    <xsl:if test="$pText">
        <xsl:value-of select="translate(substring($pText,1,1), $vLower, $vUpper)"/>

        <xsl:value-of select="substring-before(substring($pText,2), '_')"/>

        <xsl:call-template name="Pascalize">
            <xsl:with-param name="pText" select="substring-after(substring($pText,2), '_')"/>
        </xsl:call-template>

        <xsl:call-template name="GrabLastPart">
            <xsl:with-param name="pText" select="$pText"/>
        </xsl:call-template>
    </xsl:if>
</xsl:template>

<xsl:template name="GrabLastPart">
    <xsl:param name="pText"/>

    <xsl:choose>
        <xsl:when test="contains($pText, '_')">
            <xsl:call-template name="GrabLastPart">
                <xsl:with-param name="pText" expr="substring-after($pText, '_')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="substring($pText, 2)"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

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