Re: [xsl] returning nodes which have a specific child

Subject: Re: [xsl] returning nodes which have a specific child
From: Michael Ludwig <mlu@xxxxxxxxxxxxx>
Date: Thu, 02 Jul 2009 10:53:44 +0200
jim mcgovern schrieb:
Thanks very much for that! Worked a treat.

Hi Jim,


glad to hear it helped.

I'm relatively new to xsl and to be honest a lot of it seems like a
black art.  Can you briefly describe what your solution does?

Sure, see comments inline.


<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

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

The most important thing to understand is IMHO the so-called identity template (also "copy template"), of which you see the canonical form above. This copies the input unaltered. (The input is not the literal XML, but the input tree, that is what the XSL processor makes of what the XML parser produces - see XDM, XPath Data Model.)

I have an editor macro that produces the stylesheet skeleton plus
identity template, as this is almost always the right starting point
for a new stylesheet.

Note that XSLT has built-in template rules that basically lose the
markup and display the text only. Most of the time you'll want to
supplant these by the identity template. Some people even think that
the identity template should have been the built-in template rule.

Bottom line, start with the identity template.

 <xsl:template match="*" priority="-0.4" ><!-- skip unwanted -->
   <xsl:apply-templates select="*"/>
 </xsl:template>

Copying input to output is fine, but most of the time you want to make some changes. So you insert other templates (rules) that take priority over the identity template.

Priority is expressed as a decimal number. It is implicit from the match
pattern. In the above case, the match pattern has the same priority
(-0.5) as that of the identity template. So I give it an explicit
priority of -0.4 so that it takes precedence over the identity template
for matching nodes.

This template matches element nodes (*). So does the identity template
(by virtue of node(), which is a node test), but this one takes
precedence due to a higher priority.

It doesn't do much. You want to lose these nodes, but process their
child elements, so you don't copy anything and <apply-templates> to the
child elements, which are selected as "*". If I didn't process child
elements, well, processing would stop at this point in the input tree,
which is not what I want. I want processing to go all the way downtree.

 <xsl:template match="*[*[@name='CONTENT']]" ><!-- keep wanted -->
   <xsl:copy>
     <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
 </xsl:template>

</xsl:stylesheet>

This last template is for the nodes you want to keep. The template content should look familiar. Yes, that's the identity template implementation again. You copy the element node and then process all attributes (@*) and children (node()). They'll be picked up by either the identity template, in which case their copied, or the skip template, in which case their skipped.

Does that make things clearer?

Michael Ludwig

Current Thread