tags:

views:

26

answers:

1

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.

+2  A: 

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"&gt;
    <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"/>
Dimitre Novatchev
This answers the overall question, but there is a specific part it misses--recursively removing all empty tags... so I just need to change the "*[not(node())]" to an appropriate expression that means all nodes that have no non-empty descendant.
taotree
@taotree Next time state your question better. :) I think that it is best to make this new requirement a separate question -- it would be also an excellent question. You can use for the match pattern of the empty template this: `*[not(descendant::text())]`
Dimitre Novatchev
@taotree Updated the answer to reflect your new requirement... :)
Dimitre Novatchev