Re: [xsl] selecting associated headings based on sequence position

Subject: Re: [xsl] selecting associated headings based on sequence position
From: Wendell Piez <wapiez@xxxxxxxxxxxxxxxx>
Date: Thu, 31 Jul 2008 09:50:01 -0400
Peter,

At 12:28 AM 7/31/2008, you wrote:
I figured out a solution:

<xsl:template match="Cell">
<xsl:variable name="pos" select="position()"></xsl:variable>
<b><xsl:value-of select="/Worksheet/Table/Row[1]/Cell[position() eq
$pos]/Data"/></b>
<xsl:value-of select="Data"></xsl:value-of><br/>
</xsl:template>

It seemed difficult because I couldn't figure out how to get the
position of the current Cell element
from within the xpath expression... position() allows no arguments and
only works on the current context.

The most robust way to do this is to count the preceding siblings.


<xsl:template match="Cell">
  <xsl:variable name="pos" select="count(preceding-sibling::Cell)"/>
  <b><xsl:value-of select="/Worksheet/Table/Row[1]/Cell
                             [count(preceding-sibling::Cell) eq $pos]/Data"/>
  </b>
  <xsl:value-of select="Data"/><br/>
</xsl:template>

I say "robust" because the value that position() returns depends on how the context node is selected (specifically: the position of the current node within the current node list, that is the list of the nodes selected when the current node was selected), and this can be different from what you think if you're not careful to control it.

So it can save you fussing with edge cases, especially as your stylesheets are modified, to get into the habit of reminding yourself of the difference between what position() returns and a node's position in the source tree, and to examine the source tree directly when that's what you're interested in.

That having been said, if your selection is controlled (as it is here, by virtue of your selecting only Cell elements on the child:: axis in your XPath), you could also write this more succinctly as

<xsl:variable name="pos" select="count(preceding-sibling::Cell) + 1"/>
<b>
  <xsl:value-of select="/Worksheet/Table/Row[1]/Cell[$pos]/Data"/>
</b>

This uses a numeric predicate to select just the node whose position() is $pos.

Then Mike wrote:
You need to be aware that this won't work if there are empty cells. Excel
doesn't output empty elements to represent such cells, rather it includes
attributes on the next cell that isn't empty to give its position.

... if this is happening, it becomes more complex. One has to compute a cell's position dynamically by examining its preceding siblings. In 2.0, a function could be written for this, in 1.0 a named template.


Cheers,
Wendell



======================================================================
Wendell Piez                            mailto:wapiez@xxxxxxxxxxxxxxxx
Mulberry Technologies, Inc.                http://www.mulberrytech.com
17 West Jefferson Street                    Direct Phone: 301/315-9635
Suite 207                                          Phone: 301/315-9631
Rockville, MD  20850                                 Fax: 301/315-8285
----------------------------------------------------------------------
  Mulberry Technologies: A Consultancy Specializing in SGML and XML
======================================================================

Current Thread