Re: [xsl] position() return's only even numbers for nested elements

Subject: Re: [xsl] position() return's only even numbers for nested elements
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 9 May 2003 18:17:12 +0100
Hi Michal,

> please take a look at this xsl fragment, maybe you find a simple
> explanation to strange behaviour of position() function for nested
> elements.

When an XSLT processor builds a tree from an XML document, all the
text in the document is included in the tree. This includes
"insignificant whitespace" that appears between element nodes (purely
to make the document more readable).

So when your document is converted to a tree, the tree actually looks
like:

  uml
   +- text: "&#xA;  "
   +- package
   +- text: "&#xA;  "
   +- package
   +- text: "&#xA;  "
   +- package
   |   +- text: "&#xA;    "
   |   +- package
   |   +- text: "&#xA;    "
   |   +- ...
   +- ...

When you apply templates with just <xsl:apply-templates /> then the
processor selects *all* the children of the current node: that
includes all the whitespace. If you number the children, 1 is a text
node (whitespace), 2 is a package element, 3 is another text node, 4
is a package element and so on. This is why you only get even numbers
when you use <xsl:apply-templates />.

When you apply templates with <xsl:apply-templates select="package" />
then the processor selects only the package elements. If you number
the children, 1 is the first child package element, 2 is the second
child package element and so on. This is why you get the correct
numbering in this case.

There are three solutions to your problem.

First, you could always select the nodes that you actually want to
process (the package elements), so that you get numbering amongst
those nodes.

Second, you could strip out the insignificant whitespace from the uml
and package elements using the <xsl:strip-space> declaration. At the
top level of your stylesheet, put:

<xsl:strip-space elements="uml package" />

This tells the processor to strip out all the whitespace-only text
nodes that are children of uml or package elements. Then, when you
use <xsl:apply-templates> without a select attribute, it will still
select all the children of the uml or package element, but the tree
won't contain any whitespace so the only children selected will be
package elements.

Third, rather than using the position() function to get numbers for
the package elements, you could use the <xsl:number> instruction:

  <xsl:attribute name="position">
    <xsl:number />
  </xsl:attribute>

By default, the <xsl:number> element counts only those nodes that are
of the same kind (and have the same name, if they're elements) as the
node you're numbering, so it will ignore the text nodes in the tree.
If you use this option, the numbering will always reflect the position
of the elements in the source tree, so might not give you the
numbering you expect if you sort the packages into some other order to
process them.

The second option is probably the best because as well as making your
code easier, it also makes the tree smaller, which cuts down on the
amount of memory the processor requires, which probably speeds up the
processing (though probably unnoticeably unless your document is
huge).

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread