tags:

views:

138

answers:

2

Trying something kind of wacky in cleaning up some verbose XML, which is to remove all empty nodes recursively.

For this purpose, I consider a node to be "empty" if it has (a) no child nodes, (b) whitespace-only content, (c) only "empty" child nodes. That is, I consider the following to be "empty" because all of the leaves are empty/whitespace-only nodes:

<foo>
  <bar>
    <baz/>
  </bar>
  <quux>  </quux>
</foo>

I tried using <xsl:if test="child::node()"> in my templates, but that didn't seem to work. It's entirely possible the answer is "walk the tree yourself, silly", but it seems like the sort of thing XSL should be able to do?

I would expect

<foo>
  <bar>
    <baz/>
  </bar>
  <quux>  </quux>
  <quuux>Actual content</quuux>
</foo>

to come back as

<foo>
  <quuux>Actual content</quuux>
</foo>

with this filter I have in mind.

+2  A: 

The reason why child::node() didn't work for you was because you do have child nodes there - they're whitespace text nodes. Instead, try something like normalize-space() != ''. You probably don't want an if there, either - put it in match instead:

<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[normalize-space() = '']"/>
Pavel Minaev
+1. P.S.: See @santiiiii's answer, he validly points out a few minor glitches in yours.
Tomalak
Corrected accordingly.
Pavel Minaev
Thanks. This, with santiiii's correction, is what I wanted.
UltraNurd
+2  A: 

Pavel's answer is correct. Two small mistakes though: you need to close the xsl:copy node, and apply:templates uses select instead of match. This would be the final version:

<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="*[normalize-space() = '']"/>
santiiiii