views:

39

answers:

2

Title says it all really. Using only XSLT 1.0's string functions, how would I go about slicing off the end of a url?

So from

http://stackoverflow.com/questions/2981175/is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0

I would like to extract

is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0

Is this possible?

+3  A: 

Unfortunately there is no substring-after-last function in XSLT/XPath 1.0. So to get the last part of an URL you would have to write a recursive template as explained by Jeni Tenisson:

<xsl:template name="substring-after-last">
  <xsl:param name="string" />
  <xsl:param name="delimiter" />
  <xsl:choose>
    <xsl:when test="contains($string, $delimiter)">
      <xsl:call-template name="substring-after-last">
        <xsl:with-param name="string"
          select="substring-after($string, $delimiter)" />
        <xsl:with-param name="delimiter" select="$delimiter" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
  </xsl:choose>
</xsl:template>

This template would be called e.g. like this:

<xsl:call-template name="substring-after-last">
  <xsl:with-param name="string" select="$url" />
  <xsl:with-param name="delimiter" select="'/'" />
</xsl:call-template>
0xA3
+1  A: 

I. Using a recursively called named template:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output method="text"/>

 <xsl:template match="/">
  <xsl:call-template name="eatAllSlashes">
   <xsl:with-param name="pText" select="."/>
  </xsl:call-template>
 </xsl:template>

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

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

when applied on this XML document:

<t>http://stackoverflow.com/questions/2981175/is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0&lt;/t&gt;

produces the wanted, correct output:

is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0

II. Using the FXSL library:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my" exclude-result-prefixes="xsl my">
 <xsl:import href="iter.xsl"/>

 <xsl:output method="text"/>

  <my:condition/>
  <my:skipSlash/>

  <xsl:variable name="vfunCondition"
   select="document('')/*/my:condition"/>

  <xsl:variable name="vfunSkipSlash"
   select="document('')/*/my:skipSlash"/>

  <xsl:template match="/">
    <xsl:call-template name="iterUntil">
      <xsl:with-param name="pCond" select="$vfunCondition"/>
      <xsl:with-param name="pFun" select="$vfunSkipSlash"/>
      <xsl:with-param name="arg1" select="string(/)"/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template match="my:condition">
    <xsl:param name="arg1"/>

    <xsl:value-of select="number(not(contains($arg1, '/')))"/>
  </xsl:template>

  <xsl:template match="my:skipSlash">
    <xsl:param name="arg1"/>

    <xsl:value-of select="substring-after($arg1, '/')"/>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on this XML document:

<t>http://stackoverflow.com/questions/2981175/is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0&lt;/t&gt;

the wanted result is produced:

is-it-possible-to-slice-the-end-of-a-url-with-xslt-1-0

Do note:

  1. The template iterUntil has three parameters:

    -- pCond -- a function (template reference) that checks a condition on the current result and potentially issues a "stop signal" (1).

    -- pFun -- a function (template reference) that is used to produce the next current result from the current result (or initially from the input argument $arg1).

    -- arg1 -- the input argument on which the pFun function is initially applied.

  2. Our pCond function is the template that matches my:condition. It issues the "stop signal" (outputs 1) only if the string passed as $arg1 does not contain any '/' characters.

  3. Our pFun function is the template that matches my:skipSlash. It discards all characters up to and including the first '/' in the string $arg1

  4. The initial input argument is defined in $arg1 and is the string value from which only the text after the last '/' must be produced.

  5. The main advantage of using FXSL is that this avoids the need to code explicit recursion and the possibilities for errors doing this. Also, the template/functions are very generic and powerful and can be re-used for solving huge classes of similar problems.

Dimitre Novatchev
What does the `my:` namespace do?
Eric
@Eric: using a namespaced element that is a child of `<xsl:stylesheet>` is a well-known technique that allows the developer to define global constants that can be accessed (using a `document('')/*/prefix:name` XPath expression) by the XSLT code.
Dimitre Novatchev