Two phase processing: Do not output empty tags from phase-1 XSLT 2.0 processing
I have some complex XSLT 2.0 transformations. I'm trying to find out if there is general purpose way to ensure that no empty tags are output. So... conceptually, a final stage of processing that recursively removes all empty tags. I understand this could be done by a separate XSLT that did nothing but filter out empty tags, but I need to have it all packaged together in a single one.
This XSLT 2.0 transformation illustrates how multi-pass (in this case 2-pass) processing can be done:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="#current"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vPass1">
<xsl:apply-templates/>
</xsl:variable>
<xsl:apply-templates select="$vPass1/*" mode="non-empty"/>
</xsl:template>
<xsl:template match="text()[xs:integer(.) mod 2 eq 0]"/>
<xsl:template match="*[not(node())]" mode="non-empty"/>
</xsl:stylesheet>
when applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
It creates a result document in the first pass (which is captured in the $vPass1
variable), in which all <num>
elements with contents even integer are stripped off their content and are empty. Then, in the second pass, applied in a specific mode, all empty elements are removed.
The result of the transformation is:
<nums>
<num>01</num>
<num>03</num>
<num>05</num>
<num>07</num>
<num>09</num>
</nums>
Do note the use of modes, and the special modes #all
and #current
.
Update: The OP now wants in a comment to delete "recursively" "all nodes that have no non-empty descendant".
This can be implemented simpler using no explicit recursion. Just change:
<xsl:template match="*[not(node())]" mode="non-empty"/>
to:
<xsl:template match="*[not(descendant::text())]" mode="non-empty"/>