By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
459,509 Members | 1,288 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 459,509 IT Pros & Developers. It's quick & easy.

removal of empty nodes resulting from application of other templates

P: n/a
I'm trying to write a stylesheet which removes nodes which are empty
as a result of other template processing.

For example, given the following xml, I'd like to:
- remove all "b" elements
- remove all "a" elements which, as a result of "b" element
removal, now have no children

so, starting with:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
<a>
<b/>
</a>
<a>
<b/>
<c/>
</a>
</doc>

I'd like to end up with:

<?xml version="1.0" encoding="UTF-8"?>
<doc>
<a>
<c/>
</a>
</doc>

I've written this stylesheet:

<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="*"/>

<!-- matches all nodes and attributes -->
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>

<!-- rule for element "a", ought to reproduce "a" only if it has
children -->
<xsl:template match="//a">
<xsl:if test="count(./*) &gt; 0">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>

<!-- remove all b nodes -->
<xsl:template match="//b" priority="3" name="b-removal"/>

</xsl:stylesheet>

but the resultant output has not removed the empty "a" element:
<doc>
<a/>
<a>
<c/>
</a>
</doc>

Can anyone suggest a way to do this?

Thanks,

Ted

Feb 13 '07 #1
Share this Question
Share on Google+
3 Replies


P: n/a
On 13 Feb., 23:57, tschwa...@revasystems.com wrote:
I'm trying to write a stylesheet which removes nodes which are empty
as a result of other template processing.
How about applying that stylesheet to your data twice?

Regards,
Johannes
Feb 14 '07 #2

P: n/a
jo*************@gmx.net wrote:
>>I'm trying to write a stylesheet which removes nodes which are empty
as a result of other template processing.
XSLT 1.0 doesn't have any built-in ability to do two-pass processing.
The usual solution is to run two stylesheets in succession, or the same
one twice.

If you MUST do it in a single stylesheet, the usual workaround is to use
the EXSLT node-set function. Do a first pass outputting into a variable
to obtain a Result Tree Fragment, then use exslt:node-set() to make that
available to XSLT in a form that it can walk over again, generating the
final output. Modes may be useful in keeping the two passes separate, if
the behavior is different in one than in the other.

(XSLT 2.0 -- still not widely supported, unfortunately, since it adds a
lot of complexity along with the simplifications -- eliminates the
distinction between node-sets and RTFs, and can do what I've just
described without needing a "standard nonstandard" helper.)

--
Joe Kesselman / Beware the fury of a patient man. -- John Dryden
Feb 14 '07 #3

P: n/a
On Feb 13, 9:32 pm, Joseph Kesselman <keshlam-nos...@comcast.net>
wrote:
johanneskrue...@gmx.net wrote:
>I'm trying to write a stylesheet which removes nodes which are empty
as a result of other template processing.

XSLT 1.0 doesn't have any built-in ability to do two-pass processing.
The usual solution is to run two stylesheets in succession, or the same
one twice.

If you MUST do it in a single stylesheet, the usual workaround is to use
the EXSLT node-set function. Do a first pass outputting into a variable
to obtain a Result Tree Fragment, then use exslt:node-set() to make that
available to XSLT in a form that it can walk over again, generating the
final output. Modes may be useful in keeping the two passes separate, if
the behavior is different in one than in the other.

(XSLT 2.0 -- still not widely supported, unfortunately, since it adds a
lot of complexity along with the simplifications -- eliminates the
distinction between node-sets and RTFs, and can do what I've just
described without needing a "standard nonstandard" helper.)

--
Joe Kesselman / Beware the fury of a patient man. -- John Dryden
Thanks for the help! As I would like to apply the templates in one
pass I've successfully used the approach of using included
stylesheets and modes described above. Here are the stylesheets:
first-pass.xsl:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- matches all nodes and attributes -->
<xsl:template match="node()|@*" name="identity" mode="first-pass">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="first-pass"/>
</xsl:copy>
</xsl:template>

<xsl:template match="/">
<xsl:apply-templates mode="first-pass"/>
</xsl:template>

<!-- remove all b nodes -->
<xsl:template match="//b" priority="3" name="b-removal" mode="first-
pass"/>

</xsl:stylesheet>

second-pass.xsl:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- matches all nodes and attributes -->
<xsl:template match="node()|@*" name="identity" mode="second-pass">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="second-pass"/>
</xsl:copy>
</xsl:template>

<xsl:template match="/">
<xsl:apply-templates mode="second-pass"/>
</xsl:template>

<!-- rule for element "a", ought to reproduce a only if it has
children -->
<xsl:template match="//a" mode="second-pass">
<xsl:if test="count(./*) &gt; 0">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

main.xsl:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:include href="first-pass.xsl"/>
<xsl:include href="second-pass.xsl"/>

<xsl:template match="/">
<xsl:variable name="first">
<xsl:apply-templates mode="first-pass" />
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($first)" mode="second-
pass" />
</xsl:template>

</xsl:stylesheet>
Then with execution of main.xsl against the source xml, I get the
desired output:

<doc>
<a>
<c/>
</a>
</doc>

Thanks again.

Feb 15 '07 #4

This discussion thread is closed

Replies have been disabled for this discussion.