I've the following problem see the code example:
<xsl:if test="/Document/test/id">
<TEST1>
<xsl:value-of select="/Document/test/id"/>
</TEST1>
</xsl:if>
I've a huge xslt script with a lot of same code. I wanted to create a function but I got all the time errors. I was asking myself if that is the right solution to solve this problem.
To make the code better maintainable I want to use something like the following.
This is not xslt syntax because I don't know how, but to give an idea what I'm looking for.
...
<xsl:something "TEST1", "/Document/test/id1" />
<xsl:something "TEST2", "/Document/test/id2" />
...
The code would be better readable and maintainable, although I don't know how to solve this :-(
Any advise is welcome...
Regards Dirk
This transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vDoc" select="/"/>
<xsl:template match="/">
<xsl:sequence select="my:test(xs:QName('Test1'), /*/test/id1/string())"/>
<xsl:sequence select="my:test(xs:QName('Test2'), /*/test/id2/string())"/>
<xsl:sequence select="my:test(xs:QName('Test3'), /*/test/id3/string())"/>
<xsl:sequence select="my:test(xs:QName('Test4'), /*/test/id4/string())"/>
</xsl:template>
<xsl:function name="my:test">
<xsl:param name="pName" as="xs:QName"/>
<xsl:param name="pToPresent" as="item()?"/>
<xsl:if test="$pToPresent">
<xsl:element name="{$pName}">
<xsl:sequence select="$pToPresent"/>
</xsl:element>
</xsl:if>
</xsl:function>
</xsl:stylesheet>
When applied on the following XML document:
<Document>
<test>
<id1>Some Id1</id1>
</test>
<test>
<id2>Some Id2</id2>
</test>
<test>
<id4>Some Id4</id4>
</test>
</Document>
the wanted, correct result is produced:
<Test1>Some Id1</Test1>
<Test2>Some Id2</Test2>
<Test4>Some Id4</Test4>
Do note:
The first argument of my:test() is declared of type xs:QName, which guarantees that the value of the name attribute of the xsl:element instruction is always a valid name.
The second argument of my:test() is declared very loosely of type item()?, which gives us the freedom to provide any type of item (string or node or element, or ...) and that exactly what is provided would be reproduced "as is".
I think this is the closest to what you are going for:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="custom-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<CONTENT>
<xsl:sequence select="f:rename('TEST1', /Document/test/id1)"/>
<xsl:sequence select="f:rename('TEST2', /Document/test/id2)"/>
</CONTENT>
</xsl:template>
<xsl:function name="f:rename">
<xsl:param name="name" as="xs:string"/>
<xsl:param name="toWrap" />
<xsl:if test="$toWrap">
<xsl:element name="{$name}">
<xsl:value-of select="$toWrap"/>
</xsl:element>
</xsl:if>
</xsl:function>
</xsl:stylesheet>
When this is run on the following input:
<Document>
<test>
<id1>Hello!</id1>
<id2>Hello again!</id2>
</test>
</Document>
The output produced is:
<CONTENT>
<TEST1>Hello!</TEST1>
<TEST2>Hello again!</TEST2>
</CONTENT>
You could also try this, which is a little more verbose, but XSLT 1.0 compatible:
<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="/">
<CONTENT>
<xsl:apply-templates select="/Document/test/id1" mode="rename">
<xsl:with-param name="name" select="'TEST1'" />
</xsl:apply-templates>
<xsl:apply-templates select="/Document/test/id2" mode="rename">
<xsl:with-param name="name" select="'TEST2'" />
</xsl:apply-templates>
</CONTENT>
</xsl:template>
<xsl:template match="#* | node()" mode="rename">
<xsl:param name="name" />
<xsl:element name="{$name}">
<xsl:value-of select="." />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When run on the above sample XML, again the same output is produced.
Related
I have a file called ori.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>value2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>value5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
and another one called modifs.xml:
<?xml version="1.0" encoding="UTF-8"?>
<els>
<el2>newvalue2</el2>
<el5>newvalue5</el5>
</els>
and I would like to obtain result.xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<container>
<elA>
<el1>value1</el1>
<el2>newvalue2</el2>
</elA>
<elB>
<el3>value3</el3>
<el4>value4</el4>
<el5>newvalue5</el5>
</elB>
<elC>
<el6>value5</el6>
</elC>
</container>
</root>
I'm a beginner in XSLT.
So I started to write a stylesheet with which I'm able to change value2 into newvalue2:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="fileName" select="'modifs.xml'" />
<xsl:param name="modifs" select="document($fileName)" />
<xsl:param name="updateEl" >
<xsl:value-of select="$modifs/els/el2" />
</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//elA/el2">
<xsl:copy>
<xsl:apply-templates select="$updateEl" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
But now I have to modify this stylesheet to be able to know which elements are in modifs.xml and find them in ori.xml. I don't know how to do that. Could you help please ?
I would use a key:
<xsl:key name="ref-change" match="els/*" use="local-name()"/>
<xsl:template match="*[key('ref-change', local-name(), $modifs)]">
<xsl:copy-of select="key('ref-change', local-name(), $modifs)"/>
</xsl:template>
However, using the third argument for the key function is only supported in XSLT 2 and later thus if you use an XSLT 1 processor you need to move the logic into the template, that requires using for-each to "switch" the context document
<xsl:template match="*">
<xsl:variable name="this" select="."/>
<xsl:for-each select="$modifs">
<xsl:choose>
<xsl:when test="key('ref-change', local-name($this))">
<xsl:copy-of select="key('ref-change', local-name($this))"/>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$this">
<xsl:call-template name="identity"/>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
Put name="identity" on your identity transformation template.
I'm working on a Java based xsl-transformation (XSLT 2.0, could also be XSLT 3.0 if there is a free processor for java) with different input xml files. one input file could look like this:
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<MyElement>
<CHILD>A</CHILD>
<CHILDBEZ>ABEZ</CHILDBEZ>
<NotInteresting></NotInteresting>
</MyElement>
<MyElement>
<CHILD>B</CHILD>
<CHILDBEZ>BBEZ</CHILDBEZ>
<NotInteresting2></NotInteresting2>
</MyElement>
</TEST>
I want to copy all elements but "NotInteresting" and rename the two nodes CHILD and CHILDBEZ based on two parameters that I get from a mapping file:
the xpath expression that tells me where the text of interest is placed (in this case: TEST/MyFirstElement/CHILD and TEST/MyFirstElement/CHILDBEZ)
and the names of the elements what they should have in the output file (in this case: childName and childBez)
the mapping file:
<?xml version="1.0" encoding="UTF-8"?>
<element root="TEST">
<childName>TEST/MyElement/CHILD</childName>
<childBez>TEST/MyElement/CHILDBEZ</childBez>
</element>
desired output:
<TEST>
<MyElement>
<childName>A</childName>
<childBez>ABEZ</childBez>
</MyElement>
<MyElement>
<childName>B</childName>
<childBez>BBEZ</childBez>
</MyElement>
</TEST>
what I have so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0 "
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.apoverlag.at"
xmlns:apo="http://www.apoverlag.at">
<xsl:variable name="vars" select="document('mapping.xml')"/>
<xsl:param name="src" />
<xsl:variable name="path" xpath-default-namespace="" select="$src/path"/> <!-- = TEST/*-->
<xsl:template match="/">
<xsl:for-each select="$path">
<xsl:call-template name="buildNode">
<xsl:with-param name="currentNode" select="current()"></xsl:with-param>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="buildNode">
<xsl:param name="currentNode" />
<xsl:element name="test">
<xsl:for-each select="$vars/element/#root">
<xsl:for-each select="$vars/element/*">
<xsl:element name="{name(.)}"> <xsl:value-of select="concat($currentNode,'/',current())" />
</xsl:element>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:transform>
My problem is that:
<xsl:value-of select="concat($currentNode,'/',current())" />
gives me "/TEST/MyFirstElement/CHILD" when I try it hardcoded with:
<xsl:value-of select="$currentNode/CHILD" />
I receive my desired output. Can anyone give me a hint how to solve this problem?
I would suggest a radically different approach. To simplify, I have used a mapping document with full paths (starting with the / root node):
mapping xml
<element root="TEST">
<childName>/TEST/MyElement/CHILD</childName>
<childBez>/TEST/MyElement/CHILDBEZ</childBez>
</element>
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="mapping" select="document('mapping.xml')"/>
<xsl:key name="map" match="*" use="." />
<xsl:template match="/">
<xsl:variable name="first-pass">
<xsl:apply-templates mode="first-pass"/>
</xsl:variable>
<xsl:apply-templates select="$first-pass/*"/>
</xsl:template>
<xsl:template match="*" mode="first-pass">
<xsl:param name="parent-path"/>
<xsl:variable name="path" select="concat($parent-path, '/', name())" />
<xsl:variable name="replacement" select="key('map', $path, $mapping)" />
<xsl:element name="{if ($replacement) then name($replacement) else name()}">
<xsl:attribute name="original" select="not($replacement)"/>
<xsl:apply-templates mode="first-pass">
<xsl:with-param name="parent-path" select="$path"/>
</xsl:apply-templates>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#original='true' and not(descendant::*/#original='false')]"/>
</xsl:stylesheet>
Result, when applied to the provided input:
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
<MyElement>
<childName>A</childName>
<childBez>ABEZ</childBez>
</MyElement>
<MyElement>
<childName>B</childName>
<childBez>BBEZ</childBez>
</MyElement>
</TEST>
Consider following xml:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<base>
<a>
<b>
<c>Text 1</c>
</b>
</a>
</base>
<base>
<a>
<b>
<c>Text 2</c>
</b>
</a>
</base>
</root>
and this xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:value-of select="text()" />
<!-- i need Text 2 here -->
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
The original xml is much more nested an i don't know the exact structure. But there is a parallel node with the same structure. If my template is at //root/base[1]/a/b/c I want to reference //root/base[2]/a/b/c
However I only know that I am in some node below //root/base[1] and that there is the same node in //root/base[2].
Is there a possibility to accomplish this goal?
This stylesheet requires no extensions, and does not make any assumption on unique names; the boring part is to pass the correct "twin node" every time you call xsl:apply-templates:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]">
<xsl:with-param name="twin" select="root/base[2]"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="*">
<xsl:param name="twin"/>
<xsl:if test="text()">
<xsl:value-of select="text()" />
<xsl:value-of select="$twin/text()"/>
</xsl:if>
<xsl:for-each select="*">
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select=".">
<xsl:with-param name="twin" select="$twin/*[$pos]"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Solution with using saxon:evaluate extension (http://saxon.sourceforge.net/saxon7.9/extensions.html#evaluate)
You can also use dyn:evaluate if you are using xslt 1.0 processors (http://exslt.org/dyn/index.html):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
xmlns:saxon="http://saxon.sf.net/"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="path">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="rewritedPath" select="concat('/root/base[2]', substring($path, 11))"/>
<xsl:value-of select="text()" />
<xsl:value-of select="saxon:evaluate($rewritedPath)" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template name="constructPath">
<xsl:param name="node"/>
<xsl:choose>
<xsl:when test="$node/parent::node()/name()">
<xsl:call-template name="constructPath">
<xsl:with-param name="node" select="$node/parent::node()"/>
</xsl:call-template>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('/', $node/name())"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()" />
</xsl:stylesheet>
Here we use recursive template "constructPath" to create string and process it in evaluate() function. This will select the exact node as in the parallel branch. Hope this will help.
Try the code below. This code will work only if each base element has children with unique element name.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="root/base[1]" />
</xsl:template>
<xsl:template match="base//*">
<xsl:if test="text()">
<xsl:variable name="name" select="name()"/>
<xsl:value-of select="text()" />
<xsl:value-of select="ancestor::base/following-sibling::base[1]//*[name() = $name]" />
</xsl:if>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text()" />
However you can use dyn:evaluate from exslt extension or saxon:eval
My xsl file:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:go="http://www.google.com"
exclude-result-prefixes="go">
<xsl:include href="SomeLibrary.xsl"/>
<xsl:template match="/">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
The SomeLibrary.xsl file:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.google.com">
<xsl:template name="SomeTemplate">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
Input xml: just use an empty XML. The result is this:
<?xml version="1.0" encoding="UTF-8"?>
<Blabla xmlns="http://www.google.com">
randomParam
</Blabla>
What I want is to have the "Blabla" node without a namespace. How can I remove it or make sure it doesn't get there, without modifying my "SomeLibrary.xsl"?
In case you don't want to edit the imported stylesheet code, the way to remove the namespace is with a two-pass transformation (which in XSLT 1.0 requires using an xxx:node-set() extension function):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vPass1" select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates select="$vrtfPass1/node()" mode="pass2"/>
</xsl:template>
<xsl:template match="*" mode="pass2">
<xsl:element name="{name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="pass2"/>
</xsl:element>
</xsl:template>
<xsl:template name="SomeTemplate" xmlns="http://www.google.com">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted result (the default namespace removed) is produced:
<Blabla>randomParam</Blabla>
Update:
The OP has indicated in a comment that he is using Xalan 2.07.
Below is almost the same solution, but with namespace and name for the xxx:node-set() function, as used in Xalan:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="xmlns:xalan="http://xml.apache.org/xalan" exclude-result-prefixes="x" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:call-template name="SomeTemplate">
<xsl:with-param name="Element" select="'randomParam'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vPass1" select="x:nodeset($vrtfPass1)"/>
<xsl:apply-templates select="$vrtfPass1/node()" mode="pass2"/>
</xsl:template>
<xsl:template match="*" mode="pass2">
<xsl:element name="{name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="pass2"/>
</xsl:element>
</xsl:template>
<xsl:template name="SomeTemplate" xmlns="http://www.google.com">
<xsl:param name="Element"/>
<Blabla>
<xsl:value-of select="$Element" />
</Blabla>
</xsl:template>
</xsl:stylesheet>
Explicitly create them with:
<xsl:element name="Blabla" namespace="">...</xsl:element>
but this means fiddling with the SomeLibrary.xsl file.
add exclude-result-prefixes="go" to the stylesheet element.
and, of course, update the namespace declaration from the default one to xmlns:go="http://www.google.com"
I have a list of items:
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
and I want as output
z
c
x
a
I have no order information in the file and I just want to reverse the lines. The last line in the source file should be first line in the output. How can I solve this problem with XSLT without sorting by the content of the items, which would give the wrong result?
I will present two XSLT solutions:
I. XSLT 1.0 with recursion Note that this solution works for any node-set, not only in the case when the nodes are siblings:
This transformation:
<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="/*">
<xsl:call-template name="reverse">
<xsl:with-param name="pList" select="*"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="pList"/>
<xsl:if test="$pList">
<xsl:value-of
select="concat($pList[last()], '
')"/>
<xsl:call-template name="reverse">
<xsl:with-param name="pList"
select="$pList[not(position() = last())]"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<t>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</t>
produces the wanted result:
z
c
x
a
II. XSLT 2.0 solution :
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:value-of select="reverse(*)/string(.)"
separator="
"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document, the same correct result is produced.
XML CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<device>
<element>a</element>
<element>x</element>
<element>c</element>
<element>z</element>
</device>
XSLT CODE:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//device">
<xsl:for-each select="element">
<xsl:sort select="position()" data-type="number" order="descending"/>
<xsl:text> </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
note: if you're using data-type="number", and any of the values aren't numbers, those non-numeric values will sort before the numeric values. That means if you're using order="ascending", the non-numeric values appear first; if you use order="descending", the non-numeric values appear last.
Notice that the non-numeric values were not sorted; they simply appear in the output document in the order in which they were encountered.
also, you may find usefull to read this:
http://docstore.mik.ua/orelly/xml/xslt/ch06_01.htm
Not sure what the full XML looks like, so I wrapped in a <doc> element to make it well formed:
<doc>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</doc>
Running that example XML against this stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="doc/item[position()=last()]" />
</xsl:call-template>
</xsl:template>
<xsl:template name="reverse">
<xsl:param name="item" />
<xsl:value-of select="$item" />
<!--Adds a line feed-->
<xsl:text>
</xsl:text>
<!--Move on to the next item, if we aren't at the first-->
<xsl:if test="$item/preceding-sibling::item">
<xsl:call-template name="reverse">
<xsl:with-param name="item" select="$item/preceding-sibling::item[1]" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Produces the requested output:
z
c
x
a
You may need to adjust the xpath to match your actual XML.
Consider this XML input:
<?xml version="1.0" encoding="utf-8" ?>
<items>
<item>a</item>
<item>x</item>
<item>c</item>
<item>z</item>
</items>
The XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="/items[1]">
<xsl:variable name="items-list" select="." />
<xsl:variable name="items-count" select="count($items-list/*)" />
<xsl:for-each select="item">
<xsl:variable name="index" select="$items-count+1 - position()"/>
<xsl:value-of select="$items-list/item[$index]"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
And the result:
z
c
x
a