I have an XML file as such:
<a>
<b>
<e />
<f />
</b>
<c>
<g>
<j />
</g>
<g>
<j />
</g>
<g>
<j />
</g>
</c>
<d>
<h>
<i />
</h>
<h>
<i />
</h>
<h>
<i />
</h>
</d>
</a>
What I'm trying to do is to apply an XSL transformation to only get the last nodes of c and d (including their child nodes) along with the rest of the file, resulting in:
<a>
<b>
<e />
<f />
</b>
<c>
<g>
<j />
</g>
</c>
<d>
<h>
<i />
</h>
</d>
</a>
I am not experienced with XSLT and any help is greatly appreciated.
It's generally best to start with an identity transform and add exceptions, and then sometimes exceptions to the exceptions.
In this transform, the first template is the identity transform, the second skips children of <c>
and <d>
, and the third overrides that exclusion to include the last child of each <c>
and <d>
tag.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="c/*|d/*"/>
<xsl:template match="c/*[last()]|d/*[last()]">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I had to modify your input xml to remove some spaces. The construct <x/ >
is not really valid according to the specification (section 3.1 Start-tags, end-tags, and empty-element tags).
As noted in comments, this can be made shorter, using only one template in addition to the identity. I wasn't able to get [not(last()]
to work, but this shorter template does:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="c/*[position() < last()]|d/*[position() < last()]"/>
</xsl:stylesheet>
and there may be improvements possible in the condition.
Which is better is of course a matter of taste. I find my original response slightly clearer.