Search code examples
xsltxslt-1.0xslt-2.0

How can I strip trailing full stop (period) from text nodes


if I have a text node with a trailing full stop (or period in US english) what expression can I use to strip the full stop and leave the remainder?

e.g input

<ol>
<li>This is the first item.</li>
<li>This is the second</li>
<li>This is the 3rd. </li>
</ol> 

required output

<ol>
<li>This is the first item</li>
<li>This is the second</li>
<li>This is the 3rd</li>
</ol>  

I have this but it seems unnecessarily cumbersome

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <xsl:apply-templates select="ol"/>
    </xsl:template>
    
    <xsl:template match="ol">
        <ol>
            <xsl:apply-templates select="li"/>
        </ol>
    </xsl:template>
    
    <xsl:template match="li">
        <li><xsl:apply-templates select="text()" mode="clean-text"/></li>
    </xsl:template>
    
    <xsl:template match="text()" mode="clean-text">
        <xsl:variable name="normal-text" select="normalize-space(.)"/>
        <xsl:choose>
            <xsl:when test="substring($normal-text,string-length($normal-text),1) = '.'"><xsl:value-of select="normalize-space(substring($normal-text,1,string-length($normal-text)-1))"/></xsl:when>
            <xsl:otherwise><xsl:value-of select="$normal-text"/></xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Is there a cleverer way to achieve the same thing? BTW I am using v1.0 as this may be instantiated in a Microsoft environment. But a v2.0 solution would be of interest too

TIA


Solution

  • In XSLT 2 or 3 you have the replace function:

      <xsl:template match="ol/li[ends-with(normalize-space(.), '.')]">
          <xsl:copy>
              <xsl:value-of select="replace(., '\.\s*$', '')"/>
          </xsl:copy>
      </xsl:template>
    

    In XSLT 1 enviroments you can often call into the underlying platform (e.g. Java or .NET or PHP or Python) to make use of similar string functions supporting regular expressions like \.\s*$ to match on the end of a string preceded by zero or more whitespace characters preceded by a single full stop character.

    Or try to do it all with pure XPath 1 string functions

      <xsl:template match="ol/li[substring(normalize-space(), string-length(normalize-space())) =  '.']">
          <xsl:copy>
              <xsl:value-of select="substring(normalize-space(), 1, string-length(normalize-space()) - 1)"/>
          </xsl:copy>
      </xsl:template>
    

    In all cases, handle copying other stuff through by the identity transformation template: https://xsltfiddle.liberty-development.net/jxNakAW