【问题标题】:How can I trim space in XSLT without replacing repating whitespaces by single ones?如何在不替换单个空格的情况下修剪 XSLT 中的空间?
【发布时间】:2012-12-20 14:24:58
【问题描述】:

函数normalize-space 用一个空格替换空格序列并修剪提供的字符串。我怎样才能只修剪字符串而不替换空格?有像orcl:left-trim 这样的专有解决方案,但我正在寻找一个非专有的解决方案。

示例:

<xsl:value-of select="trim(/Car/Description)"/>

应该转

<car>
  <description>  To get more information look at:     www.example.com </description>
</car>

进入

"To get more information look at:     www.example.com"

【问题讨论】:

    标签: xml xslt xpath


    【解决方案1】:

    仅使用 xslt 1.0 模板的解决方案:

    <xsl:variable name="whitespace" select="'&#09;&#10;&#13; '" />
    
    <!-- Strips trailing whitespace characters from 'string' -->
    <xsl:template name="string-rtrim">
        <xsl:param name="string" />
        <xsl:param name="trim" select="$whitespace" />
    
        <xsl:variable name="length" select="string-length($string)" />
    
        <xsl:if test="$length &gt; 0">
            <xsl:choose>
                <xsl:when test="contains($trim, substring($string, $length, 1))">
                    <xsl:call-template name="string-rtrim">
                        <xsl:with-param name="string" select="substring($string, 1, $length - 1)" />
                        <xsl:with-param name="trim"   select="$trim" />
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$string" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    
    <!-- Strips leading whitespace characters from 'string' -->
    <xsl:template name="string-ltrim">
        <xsl:param name="string" />
        <xsl:param name="trim" select="$whitespace" />
    
        <xsl:if test="string-length($string) &gt; 0">
            <xsl:choose>
                <xsl:when test="contains($trim, substring($string, 1, 1))">
                    <xsl:call-template name="string-ltrim">
                        <xsl:with-param name="string" select="substring($string, 2)" />
                        <xsl:with-param name="trim"   select="$trim" />
                    </xsl:call-template>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$string" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    
    <!-- Strips leading and trailing whitespace characters from 'string' -->
    <xsl:template name="string-trim">
        <xsl:param name="string" />
        <xsl:param name="trim" select="$whitespace" />
        <xsl:call-template name="string-rtrim">
            <xsl:with-param name="string">
                <xsl:call-template name="string-ltrim">
                    <xsl:with-param name="string" select="$string" />
                    <xsl:with-param name="trim"   select="$trim" />
                </xsl:call-template>
            </xsl:with-param>
            <xsl:with-param name="trim"   select="$trim" />
        </xsl:call-template>
    </xsl:template>
    

    测试代码:

    <ltrim>
        <xsl:call-template name="string-ltrim">
            <xsl:with-param name="string" select="'   &#10;   test  '" />
        </xsl:call-template>
    </ltrim>
    <rtrim>
        <xsl:call-template name="string-rtrim">
            <xsl:with-param name="string" select="'    &#10;    test  &#10;  '" />
        </xsl:call-template>
    </rtrim>
    <trim>
        <xsl:call-template name="string-trim">
            <xsl:with-param name="string" select="'    &#10;    test  &#10;  '" />
        </xsl:call-template>
    </trim>
    

    输出:

    <test>
        <ltrim>test  </ltrim>
        <rtrim>   
        test</rtrim>
        <trim>test</trim>
    </test>
    

    【讨论】:

    • 我喜欢无需添加额外库的简洁解决方案 - 谢谢您!由于我必须先询问项目经理,我们是否可以添加额外的库,这个解决方案显然更适合我。我想没有办法简单地在 xsl 文件的开头放置一个参数来修剪所有字段,是吗?我找到了&lt;xsl:strip-space&gt;,但这仅在之后 应用concat(...) 等其他转换,导致结果类似于Lastname , Firstname 而不是Lastname, Firstname。好像没有这么简单的解决方案……
    • @MathiasBader: xsl:strip-space 在进行任何转换之前对源文档进行操作,并删除仅包含空格的文本节点。它可能不会解决你的问题。最简单的解决方案可能是创建一个单独的转换,只修剪所选元素的空白,并将其结果用作第二个转换的输入。这可以使用特定处理器的扩展函数在单个样式表中完成,或者使用外部工具/api 来调用转换。
    • 通过外部工具使用单独的(预)转换也是我的想法。我只是认为这个问题似乎很普遍,我认为 XSLT 提供了一个我还没有找到的更简单的解决方案。
    【解决方案2】:

    normalize-space(actualSting) - 这样就可以了。

    【讨论】:

    • 这也会删除中间的重复空格。我一直在寻找不这样做的解决方案。
    【解决方案3】:

    使用FXSL(XSLT 函数式编程的开源库,完全用 XSLT 编写)一个简单的写法

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:import href="trim.xsl"/>
    
      <xsl:output method="text"/>
      <xsl:template match="/*/description">
        '<xsl:call-template name="trim">
            <xsl:with-param name="pStr" select="."/>
        </xsl:call-template>'
      </xsl:template>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时:

    <car>
      <description>  To get more information look at:     www.example.com </description>
    </car>
    

    产生了想要的正确结果:

    'To get more information look at:     www.example.com'
    

    trim 模板如何工作

    它修剪左边的前导空格,然后反转生成的字符串并修剪其前导空格,最后反转生成的字符串。


    二。 XPath 2.0 解决方案

    使用

    replace(replace(/*/description, '^\s*(.+?)\s*$', '$1'), '^ .*$', '')
    

    这是一个基于 XSLT - 2.0 的验证:

    <xsl:stylesheet version="2.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
         "<xsl:sequence 
          select="replace(replace(/*/description, '^\s*(.+?)\s*$', '$1'), '^ .*$', '')"/>"
     </xsl:template>
    </xsl:stylesheet>
    

    在提供的 XML 文档(上图)上应用此转换时,将评估 XPath 表达式并将此评估的结果复制到输出:

     "To get more information look at:     www.example.com"
    

    【讨论】:

    • 感谢您的回答,这听起来是个不错的解决方案。没有不需要额外库的“本机”解决方案(当然,最好导入额外的库),我说得对吗?
    • @MathiasBader,首先,使用 FXSL一种原生解决方案——FXSL 是一组 XSLT 模板。如果您不想使用 FXSL,可以编写一个递归 XSLT 解决方案——当您可以调用已经存在的(和经过测试的)FXSL 解决方案时这样做是不明智的。如果您不想节省几个小时或一天的额外工作,并确保解决方案中没有错误,请从头开始编写转换。 FXSL 的目的正是为了尽量减少为非常困难或“几乎不可能”的问题编写解决方案的工作量。
    • @MathiasBader,换句话说,我想要这个答案: 1. 为您节省大量时间和开发/验证工作。 2. 让您知道,有一种工具可以显着缩短大多数任务的 XSLT 开发时间,并使使用 XSLT 非常困难(“几乎不可能”)的任务变得非常自然和简单。
    • 修剪正则表达式似乎不适用于纯空格字符串 - 假设 '' 是修剪纯空格字符串的结果。这种替代方法似乎对我有用replace(replace($pStr, '^\s\s*', ''), '\s\s*$', '')
    • @dave,很好——谢谢!我使用正则表达式编辑了答案,该表达式在“常规”情况和纯空格字符串的情况下都产生了正确的结果。
    【解决方案4】:

    一个非常简短的 XSLT1 解决方案:

    <xsl:template name="trim">
                <xsl:param name="str"/>
    
                <xsl:choose>
                    <xsl:when test="string-length($str) &gt; 0 and substring($str, 1, 1) = ' '">
                        <xsl:call-template name="trim"><xsl:with-param name="str"><xsl:value-of select="substring($str, 2)"/></xsl:with-param></xsl:call-template></xsl:when>
                    <xsl:when test="string-length($str) &gt; 0 and substring($str, string-length($str)) = ' '">
                        <xsl:call-template name="trim"><xsl:with-param name="str"><xsl:value-of select="substring($str, 1, string-length($str)-1)"/></xsl:with-param></xsl:call-template></xsl:when>
                    <xsl:otherwise><xsl:value-of select="$str"/></xsl:otherwise>
                </xsl:choose>
            </xsl:template>
    

    【讨论】:

      【解决方案5】:

      如果中间没有空格,可以直接使用:

      translate(/Car/Description,' ','')
      

      【讨论】:

      • 好主意,但正如您可能已经猜到的那样,不幸的是,这过于简化了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多