Reuse variable defined inside choose, outside of choose in XSLT - xslt

I have a piece of code that look similar to this:
<xsl:choose>
<xsl:when test="some_test">
<xsl:value-of select="Something" />
You are:
<xsl:variable name="age">12</xsl:variable>
years
</xsl:when>
</xsl:choose>
My problem is that I would like to use the variable $age outside of the choose. How do I do that?
A similar problem is that I have several templates in my XSLT-file, and one of them is the main template. This one:
<xsl:template match="/">
</xsl:template>
Inside of this template, I call several other templates, and again I would like to use some of the variables from the other templates.
In example, if I have this code:
<xsl:template match="/">
<xsl:call-template name="search">
</xsl:call-template>
</xsl:template>
<xsl:template name="search">
<xsl:variable name="searchVar">Something...</xsl:variable>
</xsl:template>
Then I would like to use the $searchVar inside of my main-template.
It's kind of the same issue I think, but I can't seem to figure this one out.
And I run Umbraco as my CMS by the way :)
I hope that some of you have the answer.
Thanks
- Kim

To #1: Variables are valid within their parent element only. Which means you must put the logic inside the variable instead of "around" it:
<xsl:variable name="var">
<xsl:choose>
<xsl:when test="some_test">
<xsl:text>HEY!</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>SEE YA!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
To #2: Use parameters to transport values into templates.
<xsl:template match="/">
<xsl:variable name="var" select="'something'" />
<!-- params work for named templates.. -->
<xsl:call-template name="search">
<xsl:with-param name="p" select="$var" />
</xsl:call-template>
<!-- ...and for normal templates as well -->
<xsl:apply-templates select="xpath/to/nodes">
<xsl:with-param name="p" select="$var" />
</xsl:apply-templates>
</xsl:template>
<!-- named template -->
<xsl:template name="search">
<xsl:param name="p" />
<!-- does stuff with $p -->
</xsl:template>
<-- normal template -->
<xsl:template match="nodes">
<xsl:param name="p" />
<!-- does stuff with $p -->
</xsl:template>
To transport a value back to the calling template, combine the above:
<xsl:template match="/">
<xsl:variable name="var">
<xsl:call-template name="age">
<xsl:with-param name="num" select="28" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$var" />
</xsl:template>
<xsl:template name="age">
<xsl:param name="num" />
<xsl:value-of select="concat('You are ', $num, ' years yold!')" />
</xsl:template>

Have a look at xsl:param.
Edit: Untested, but may work:
<xsl:param name="var">
<xsl:choose>
<xsl:when test="some_test">
<xsl:value-of select="string('HEY!')" />
</xsl:when>
</xsl:choose>
</xsl:param>

Related

Is there a way to use mode in an xsl:if test?

I've been learning to use the mode attribute with XSLT and was wondering if there's a way to test for it within a template, such as in an xsl:if statement? I've only seen it used at the xsl:template level and maybe that's the only way. Say I want to add a "../" in front of a path attribute (#href), but only if mode="print":
<xsl:template name="object" mode="#all">
<img>
<xsl:attribute name="src">
<xsl:if test="mode='print'"><xsl:text>../</xsl:text></xsl:if>
<xsl:value-of select="#href"/>
</xsl:attribute>
</img>
</xsl:template>
I'm calling apply-templates with and without the mode="print" set from various other templates.
Of course I can make a new template with mode="print" but then I'd have to maintain two templates.
Or maybe there's a better way to do this? Thanks for the help. - Scott
There is no direct way to do it yet. One approach can be-
<xsl:template match="/">
<xsl:apply-templates select="something" mode="a">
<xsl:with-param name="mode" select="'a'" tunnel="yes"/>
</xsl:apply-templates>
<xsl:apply-templates select="something" mode="b">
<xsl:with-param name="mode" select="'b'" tunnel="yes"/>
</xsl:apply-templates>
</xsl:template>
and then in the match-
<xsl:template match="blah" mode="a b">
<xsl:param name="mode" tunnel="yes"/>
<xsl:if test="$mode='a'">
<!-- Do Something -->
</xsl:if>
<xsl:if test="$mode='b'">
<!-- Do Something -->
</xsl:if>
</xsl:template>
There's no way to get the current mode, but you can do this:
<xsl:template match="object" mode="#all">
<xsl:param name="print" select="false()"/>
<!-- Your code here -->
</xsl:template>
<xsl:template match="object" mode="print">
<xsl:next-match>
<xsl:with-param name="print" select="true()"/>
</xsl:next-match>
</xsl:template>

Reorder nodes returned from xsl:choose

What would be an efficient way to reorder a group of nodes selected using xsl:choose (XSLT 1.0).
Below is the sample source XML:
<Universe>
<CObj>
<Galaxies>
<Galaxy>
<Profile>
<Name>MilkyWay</Name>
<Age>12.5</Age>
</Profile>
<PlanetarySystem>
<Name>Solar</Name>
<Location></Location>
<Planet>
<Name>Earth</Name>
<Satellite>Y</Satellite>
...
...
...
</Planet>
...
...
...
</PlanetarySystem>
<PlanetarySystem>
...
...
...
</PlanetarySystem>
</Galaxy>
<Galaxy>
...
...
...
</Galaxy>
</Galaxies>
</CObj>
</Universe>
XSL snippet:
<xsl:template name="get-galaxy-types">
<xsl:variable name="galaxy_age1" select ="1" />
<xsl:variable name="galaxy_age2" select ="5" />
<xsl:variable name="galaxy_age3" select ="10" />
<xsl:for-each select="Galaxies/Galaxy/Profile/Age">
<xsl:choose>
<xsl:when test=".=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test=".=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType3>
</xsl:when>
<xsl:when test=".=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="../Profile/Name"/>
</GalaxyType1>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Above XSL template is called from main template like:
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:call-template name="get-galaxy-types"/>
</GalaxyTypes>
</xsl:template>
Output XML:
Note that the order of <GalaxyType> cannot be changed.
<Universe>
...
...
...
<GalaxyTypes>
<GalaxyType2>xxxxxx</GalaxyType2>
<GalaxyType3>xxxxxx</GalaxyType3>
<GalaxyType1>xxxxxx</GalaxyType1>
</GalaxyTypes>
...
...
...
</Universe>
Since xsl:choose returns the XML nodes as and when it finds a match I am unable to find a straight forward way to control the order in which I want GalaxyType to appear in the output XML.
How can I have a generic template to perform reordering for any elements that might get added in the future that may fall in to similar requirement. I am fine with having a remapping template within this XSL but I am not really sure how to accomplish this in a really elegant and efficient way.
I am going to guess you want to put the galaxies matching galaxy-age1 first, then the ones matching galaxy-age2 next, and finally the ones for galaxy-age3. I am also assuming the ages specified may not be in ascending order (that is to say, galaxy-age3 could be less that galaxy-age1.
To start with, it might be more natural to do your xsl:for-each over the Galaxy elements
<xsl:for-each select="Galaxies/Galaxy">
Then, to do your customisable sort, you could first define a variable like so...
<xsl:variable name="sortAges"
select="concat('-', $galaxy_age1, '-', $galaxy_age2, '-', $galaxy_age3, '-')" />
Note the order the parameters appear in the concat statement corresponds to the order they need to be output.
Then, your xsl:for-each could look this this...
<xsl:for-each select="Galaxies/Galaxy">
<xsl:sort select="string-length(substring-before($sortAges, concat('-', Profile/Age, '-')))" />
But this is not very elegant. It might be better to simply have a template that matches Galaxy and use three separate xsl:apply-templates to select the Galaxy; one for each age.
Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="5" />
<xsl:param name="galaxy_age3" select="10" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age1]">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age2]">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="Galaxies/Galaxy[Profile/Age = $galaxy_age3]">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
EDIT: To make this more efficient, consider using a key to look up the Galaxy elements by their name. Try this XSLT too
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:param name="galaxy_age1" select="1" />
<xsl:param name="galaxy_age2" select="10" />
<xsl:param name="galaxy_age3" select="5" />
<xsl:key name="galaxy" match="Galaxy" use="Profile/Age" />
<xsl:template match="Universe">
<GalaxyTypes>
<xsl:apply-templates select="key('galaxy', $galaxy_age1)">
<xsl:with-param name="num" select="1" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age2)">
<xsl:with-param name="num" select="2" />
</xsl:apply-templates>
<xsl:apply-templates select="key('galaxy', $galaxy_age3)">
<xsl:with-param name="num" select="3" />
</xsl:apply-templates>
</GalaxyTypes>
</xsl:template>
<xsl:template match="Galaxy">
<xsl:param name="num" />
<xsl:element name="Galaxy{$num}">
<xsl:value-of select="Profile/Name"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It's very difficult to navigate between the scattered snippets of your code. Still, it seems to me you should change your strategy to something like:
<xsl:template match="?">
...
<GalaxyTypes>
<xsl:apply-templates select="??/???/Galaxy">
<xsl:sort select="Profile/Age" data-type="number" order="ascending"/>
</xsl:apply-templates=>
</GalaxyTypes>
...
</xsl:template>
<xsl:template match="Galaxy">
<xsl:choose>
<xsl:when test="Profile/Age=$galaxy_age1">
<GalaxyType1>
<xsl:value-of select="Profile/Name"/>
</GalaxyType1>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age2">
<GalaxyType2>
<xsl:value-of select="Profile/Name"/>
</GalaxyType2>
</xsl:when>
<xsl:when test="Profile/Age=$galaxy_age3">
<GalaxyType3>
<xsl:value-of select="Profile/Name"/>
</GalaxyType3>
</xsl:when>
</xsl:choose>
</xsl:template>
--
Note that your output would be much better formatted if all galaxies were a uniform <Galaxy> element, with a type attribute to tell them apart.

How XSLT treats multiple nested element?

I want to convert some plain text with special marker into HTML formatted text.
For example,
This is the original value
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
The original value as actual value (just for reference)
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text.
HTML format I expect as a result
Th<i>is is a <u>com<b>bina</b>tion</u></i> text.
I tried with below template but it can not be parsed by XSLT parser.
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
</xsl:when>
<xsl:when test="$tagName='/bold'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
</xsl:when>
<xsl:when test="$tagName='/italic'">
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
</xsl:when>
<xsl:when test="$tagName='/under'">
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="substring-after($postString, '>')" />
</xsl:call-template>
</xsl:template>
any idea to solve this?
I would appreciate in advance.
If you can actually keep the original text, your life would be easier. Then you can transform
Th<italic>is is a <under>com<bold>bina</bold>tion</under></italic> text. easily into xHTML
<xsl:template match="italic">
<xsl:element name="i">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
You can't mix your tags in your example. You open an xsl:when and open an xsl:element and then close the xsl:when. That is not valid XML!
So if you want to go with the encoded string you need something like:
<xsl:template name="decorateValue">
<xsl:param name="originalString" />
<xsl:if test="$originalString!=''">
<xsl:variable name="preString" select="substring-before($originalString, '<')" />
<xsl:variable name="postString" select="substring-after($originalString, '<')" />
<xsl:variable name="endString" select="'magic happens here!'" />
<xsl:value-of select="$preString" />
<xsl:variable name="tagName" select="substring-before($postString, '>')" />
<xsl:variable name="restString" select="'magic happens here!'" />
<xsl:choose>
<xsl:when test="$tagName='bold'">
<xsl:element name="b">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='italic'">
<xsl:element name="i">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:when test="$tagName='under'">
<xsl:element name="u">
<xsl:call-template name="decorateValue">
<xsl:with-param name="originalString"
select="$restString" />
</xsl:call-template>
</xsl:element>
</xsl:when>
</xsl:choose>
<xsl:value-of select="$endString" />
</xsl:if>
</xsl:template>
You see 2 places where 'magic happens here'. This is where you need to apply the string-before-last and string-after-last patterns (which are a PITA in XSLT). The best explanation can be found in the XSLT Cookbook (and you want to have XSLT 2.0 at least.
Hope the pointers help. You might consider breaking the pattern into individual, so you don't fish for > alone, but the full tags. You still need to use the before-last / after-last functions.

Simple XSLT template

There is XML document:
<data>how;many;i;can;tell;you</data>
Need to get XML using XSLT version 1:
<manydata>
<onedata>how</onedata>
<onedata>many</onedata>
<onedata>i</onedata>
<onedata>can</onedata>
<onedata>tell</onedata>
<onedata>you</onedata>
</manydata>
How I can do it?
UPDATE:
Output format must be XML.
This recursive solution is probably one of the shortest possible:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="data">
<manydata><xsl:apply-templates/></manydata>
</xsl:template>
<xsl:template match="text()" name="tokenize">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText)">
<onedata>
<xsl:value-of select=
"substring-before(concat($pText,';'),';')"/>
</onedata>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText,';')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document;
<data>how;many;i;can;tell;you</data>
the wanted, correct result is produced:
<manydata>
<onedata>how</onedata>
<onedata>many</onedata>
<onedata>i</onedata>
<onedata>can</onedata>
<onedata>tell</onedata>
<onedata>you</onedata>
</manydata>
<xsl:template match="data">
<manydata>
<!--
empty <manydata/> will be generated,
if <data/> without child text()
-->
<xsl:apply-templates select="text()"/>
</manydata>
</xsl:template>
<xsl:template match="data/text()">
<!-- start point for recursion -->
<xsl:call-template name="tokenize-string">
<xsl:with-param name="separator" select="';'"/>
<xsl:with-param name="string" select="text()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize-string">
<xsl:param name="separator"/>
<xsl:param name="string"/>
<xsl:variable name="string-before-separator"
select="substring-before( $string, $separator )"/>
<onedata>
<xsl:choose>
<!-- if $separator presents in $string take first piece -->
<xsl:when test="$string-before-separator">
<xsl:value-of select="$string-before-separator"/>
</xsl:when>
<!-- take whole $string, if no $separator in the $string -->
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</onedata>
<!-- recursive call, if separator was found -->
<xsl:if test="$string-before-separator">
<xsl:call-template name="tokenize-string">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="string"
select="substring-after( $string, $separator )"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:element name="manydata">
<xsl:call-template name="splitsemicolons">
<xsl:with-param name="text" select="text()" />
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="splitsemicolons">
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="contains($text,';')">
<xsl:element name="onedata">
<xsl:value-of select="substring-before($text,';')" />
</xsl:element>
<xsl:call-template name="splitsemicolons">
<xsl:with-param name="text" select="substring-after($text,';')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="onedata">
<xsl:value-of select="$text" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This uses a named template that is called recursively, each time outputting what's before the first ;, and calling itself with everything after the first ;. If there isn't a ;, it just outputs whatever's left as-is.
You can use the XSLT extension library to get the tokenize function. Here is how the final code will look like:
<xsl:template match="/">
<manydata>
<xsl:for-each select="str:tokenize(data, ';')">
<xsl:value-of select="." />
</xsl:for-each>
</manydata>
</xsl:template>
</xsl:stylesheet>
Please note you will have to import the extension library into you XSLT using:
<xsl:import href="path/str.xsl" />
before you use the library functions.

XSL - Removing the filename from the path string

I've got a SharePoint problem which I need some help with. I'm creating some custom ItemStyles to format the output of a Content Query Webpart (CQWP) but I need to insert a "view all" button into the output.
View all needs to point to:
http://www.site.com/subsite/doclibrary1/Forms/AllItems.aspx
All the individual files in the document library have the link of:
http://www.site.com/subsite/doclibrary1/FileName.doc
So what I need is some XSL functions to strip FileName.doc from the end of the string.
I've tried using substring-before($variable, '.') to get rid of the .doc, but I then need to find a way to use substring-after to search for the LAST forward slash in the series and truncate the orphaned filename.
Using #Mads Hansen's post, this is the code which resolved the problem:
Template in ItemStyle.xsl
<xsl:template name="ImpDocs" match="Row[#Style='ImpDocs']" mode="itemstyle">
<xsl:variable name="SafeLinkUrl">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="ViewAllLink">
<xsl:call-template name="OuterTemplate.getCleanURL">
<xsl:with-param name="path" select="#LinkUrl"/>
</xsl:call-template>
</xsl:variable>
<div class="DocViewAll">
View All
<!--Any other code you need for your custom ItemStyle here-->
</div>
</xsl:template>
Template in ContentQueryMain.xsl
<xsl:template name="OuterTemplate.getCleanURL">
<xsl:param name="path" />
<xsl:choose>
<xsl:when test="contains($path,'/')">
<xsl:value-of select="substring-before($path,'/')" />
<xsl:text>/</xsl:text>
<xsl:call-template name="OuterTemplate.getCleanURL">
<xsl:with-param name="path" select="substring-after($path,'/')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
Executing this stylesheet produces: http://www.site.com/subsite/doclibrary1/
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:call-template name="getURL">
<xsl:with-param name="path">http://www.site.com/subsite/doclibrary1/FileName.doc</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="getURL">
<xsl:param name="path" />
<xsl:choose>
<xsl:when test="contains($path,'/')">
<xsl:value-of select="substring-before($path,'/')" />
<xsl:text>/</xsl:text>
<xsl:call-template name="getURL">
<xsl:with-param name="path" select="substring-after($path,'/')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The getURL template makes a recursive call to itself when there are "/" characters in the string. While there are still "/" characters, it spits out the values before the slash, and then invokes itself. When it reaches the last one, it stops.
The given solutions are not able to handle url's without filename and extension at the end (Path to folder)
I changed the ideas above to include this aswell...
<xsl:template name="getPath">
<xsl:param name="url" />
<xsl:choose>
<xsl:when test="contains($url,'/')">
<xsl:value-of select="substring-before($url,'/')" />
<xsl:text>/</xsl:text>
<xsl:call-template name="getPath">
<xsl:with-param name="url" select="substring-after($url,'/')" />
</xsl:call-template>
</xsl:when >
<xsl:otherwise>
<xsl:if test="not(contains($url,'.'))">
<xsl:value-of select="$url" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Btw. Why does MS still not support XSLT 2.0!, i saw people complainin bout that back in 2007 -.-'
If you are using XSLT 2.0 (or more specifically, XPath 2.0), then you should be able to use the replace function, using a regular expression to capture the substring before the last "/":
http://www.w3.org/TR/xpath-functions/#func-replace
Unfortunately, "replace" did not exist in XSLT 1.0, so it depends on what XSLT processor you are using as to whether this will work for you.
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="url">
<xsl:variable name="vReverseUrl">
<xsl:call-template name="reverse"/>
</xsl:variable>
<xsl:call-template name="reverse">
<xsl:with-param name="pString"
select="substring-after($vReverseUrl,'/')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="pString" select="."/>
<xsl:if test="$pString">
<xsl:call-template name="reverse">
<xsl:with-param name="pString" select="substring($pString,2)"/>
</xsl:call-template>
<xsl:value-of select="substring($pString,1,1)"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With this input:
<url>http://www.site.com/subsite/doclibrary1/FileName.doc</url>
Output:
http://www.site.com/subsite/doclibrary1
One line XPath 2.0:
string-join(tokenize(url,'/')[position()!=last()],'/')
See my answer to this question and use the same technique (#Alejandro's answer essentially copies this).