tags:

views:

278

answers:

2

I have the current structure;

Item 1
 - Subitem 1
 - Subitem 2
 - Subitem 3
 - - Sub subitem 1
 - - Sub subitem 2
 - - Sub subitem 3
 - - etc

I have a field that is only defined on "Item 1". Let's call it 'Header'.

When i am on "Item 1" its easy enough to extract this value, but what about when i am on the other items? Is there a way i can go through the tree (up) until i find a field called 'Header' and it has a value, and then stop and use this as a reference point in my sub items?

+1  A: 

If your current context isn't Item 1 but rather one of its children, you can use the ancestor axis to get its data.

So, assuming your Item 1 element has an attribute called Header, you can do something like this:

<xsl:value-of select="ancestor::Item1/@Header"/>

This assumes your XML structure (which you really should have supplied in your question) looks like this:

<Item1 Header="a header">
  <Subitem1/>
  <Subitem2/>
  <!-- etc -->
</Item1>

And that the name "Item1" is unique in this structure.

Welbog
+1. What's missing is the explanation that `<xsl:value-of select="ancestor::Item1/@Header"/>` gets the closest @Header value by "accident". It selects all ancestor headers, and value-of looks at the first in the list only. Which happens to be the closest based on the sort order of the ancestor axis.
Tomalak
+1  A: 

If you want to look up the tree and find the closest element that contains a Header attribute with a value, something like this should work.

  <xsl:value-of select="(ancestor::*/@Header[.!=''])[last()]" />

This uses the ancestor axis to look up the tree and the xpath expression looks for any element with a Header attribute who's value is not empty.

A second filter is used to evaluate the last one. If there is more than one ancestor that has a @Header with a value, the last one would be the closest to the context node, because the result is in document order.

EDIT: An alternative way to find the same results, leveraging the fact that ancestor axis returns in reverse document order would be to put the filter criteria in the predicate for the ancestor axis step, select the first one(closest match to the context node), and then step into the @Header:

<xsl:value-of select="ancestor::*[@Header!=''][1]/@Header" />
Mads Hansen
Ahh - looks good. WOuld this code also work on "Item 1" in this example? (The item that contains the header value)?
kastru
Yes, it should work, but you can change it from matching on any element(i.e. *) to the specific element name Item1 to be a more specific match.
Mads Hansen
@Mads Hansen: The ancestor axis is sorted in reverse. The "closest" `@header` is not `[last()]`, it's `[1]`.
Tomalak
@Tomalak - You are correct that ancestor axis is reverse order, but the xpath used evaluates the @Header is in another step and those results are in document order, requiring the predicate filter for last().
Mads Hansen
@Mads: Aww snap. Of course you are right.
Tomalak