calstad
calstad

Reputation: 628

XSLT to limit the number of child nodes under a parent node

Sorry but a total xslt noob here

Given XML that looks like:

<Foo>
  <Bar />
  <Baz />
  <Qax />
  <FooBar />
</Foo>

Is there an XSLT that will limit the number of child nodes under Foo so that there are only 3?

Upvotes: 1

Views: 2102

Answers (3)

Flynn1179
Flynn1179

Reputation: 12075

There are three ways of doing this:

Firstly, you can instruct the template that handles the Foo element to only process the first three child nodes:

<xsl:template match="Foo">
  <xsl:copy> <!-- can substitute with 'Foo' in this instance -->
    <xsl:apply-templates select="*[position() &lt;= 3]" />
  </xsl:copy>
</xsl:template>

Or, if the first three child nodes don't need any individual treatment, you can use a template like this to handle the children:

<xsl:template match="Foo/*[position() &lt;= 3]">
  <!-- process the child element here -->
</xsl:template>

The two downsides to this approach are that the remaining children of Foo can be processed by other templates, for example if you had one for specifically handling FooBar nodes, and also there's no easy way of treating your Bar and Baz elements differently.

The third method is to use an empty template that is applied to all child nodes of Foo after the third one like this:

<xsl:template match="Foo/*[position() &gt; 3]" />

This method also gives you the advantage of being able to apply individual templates for Bar and Baz if you need to.

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243479

This transformation uses and overrides the identity rule/template:

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

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

 <xsl:template match="/*/*[position() > 3]"/>
</xsl:stylesheet>

When applied on the provided XML document:

<Foo>
    <Bar />
    <Baz />
    <Qax />
    <FooBar />
</Foo>

the wanted, correct result is produced:

<Foo>
   <Bar/>
   <Baz/>
   <Qax/>
</Foo>

Explanation:

  1. The identity rule/template copies every node "as-is".

  2. We have just one additional template that overrides the identity rule for any element that is a child of the top element with position greater than 3. This template dos nothing (has an empty body), which effectively prevents any such element from being copied to the output (or as we use to say, "deletes" it).

Do note:

  1. Using and overriding the identity rule is the most fundamental and powerful XSLT design pattern.

  2. Using this design pattern is recommended over a simple <xsl:copy-of>, because it allows the nodes not only to be copied, but to be processed by any template we provide. Attributes of all elements are also processed.

Upvotes: 2

Emiliano Poggi
Emiliano Poggi

Reputation: 24826

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

    <xsl:template match="/Foo">
        <xsl:copy>
            <xsl:copy-of select="*[position()&lt;4]"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

gets:

<Foo>
   <Bar/>
   <Baz/>
   <Qax/>
</Foo>

Upvotes: 0

Related Questions