Finding Relatives
When your XSLT processor is processing one node of your source tree, and you want to get something from another node, you can use an XPath expression and an xsl:value-of instruction. I'm going to hurry through the general structure of an XPath expression to get to the really useful stuff: using XPath abbreviations to get at the different "relatives" a node can have on a source tree -- siblings, a parent, grandparents and so on. This brief review of the full XPath syntax will put the abbreviations in context.
An XPath expression consists of one or more location steps separated by slashes. Each location step consists of an axis specifier, a node test, and an optional predicate. You don't have to include an axis specifier, either; if you leave it out, a default of child is assumed. This makes a node test name like "product" a perfectly valid one-step XPath location step -- in fact, it's a perfectly valid XPath expression. When you include an axis specifier, it's separated from the node test with two colons, and a predicate goes inside of square brackets, so a location step of preceding-sibling::item means "of all the nodes in the preceding-sibling axis, I want the ones named 'item'." A location step that includes a predicate, like preceding-sibling::item[1], means "of all the nodes in the preceding-sibling axis, I want the first one named 'item'."
XPath offers abbreviations that let you use much of its power without spelling out full location path steps every time you want to use them. For example, @ means the same thing as attribute:: and .. means the same thing as parent::node(). Let's see how useful these abbreviations can be and how much more flexibility you have when you use axes that have no abbreviations.
Parent, Grandparent, Sibling and other Relative Elements: Getting their Content and Attributes
In the following, the list element is a child of the prices element, which is a child of the wine element. We're going to see how a template rule that processes that list element can access many other nodes of an XSLT source tree holding this document.
<wine grape="Chardonnay">
<winery>Lindeman's</winery>
<product>Bin 65</product>
<year>1998</year>
<desc>Youthful, with a cascade of spicy fig.</desc>
<prices>
<list>6.99</list>
<discounted>5.99</discounted>
<case>71.50</case>
</prices>
</wine>
The following template rule tells the XSLT processor to add information about the list element and its various relatives to the result tree. Much of the template is text nodes: "~~~~ Start of list element's template ~~~~" to show where the template's output begins and "1. List price (current node): {" to label the results of the xsl:apply-templates element. The curly braces around the xsl:apply-templates and the xsl:value-of elements will make it easier to see where the results of these elements begin and end.
<xsl:template match="list">
~~~~ Start of list element's template ~~~~
1. List price (current node): {<xsl:apply-templates/>}
2. Parent element (prices) contents: {<xsl:value-of select=".."/>}
3. Grandparent element contents: {<xsl:value-of select="../.."/>}
4. Attribute of grandparent: {<xsl:value-of select="../../@grape"/>}
5. Sibling node {<xsl:value-of select="../discounted"/>}
6. "Uncle" node {<xsl:value-of select="../../product"/>}
7. Parent node's name: {<xsl:value-of select="name(..)"/>}
8. Grandparent node's name: {<xsl:value-of select="name(../..)"/>}
~~~~ End of list element's template ~~~~
</xsl:template>
Before we examine the template in detail, let's look at the result it creates with the wine element above as input:
~~~~ Start of list element's template ~~~~
1. List price (current node): {6.99}
2. Parent element (prices) contents: {
6.99
5.99
71.50
}
3. Grandparent element contents: {
Lindeman's
Bin 65
1998
Youthful, with a cascade of spicy fig.
6.99
5.99
71.50
}
4. Attribute of grandparent: {Chardonnay}
5. Sibling node {5.99}
6. "Uncle" node {Bin 65}
7. Parent node's name: {prices}
8. Grandparent node's name: {wine}
~~~~ End of list element's template ~~~~
Between the labels showing the beginning and end of the template's actions, it performs eight numbered steps.
-
The line labeled "1. List price" has an xsl:apply-templates element that tells the XSLT processor to apply any relevant templates to the node's children. The item node named by the xsl:template element's match attribute has only one child: a text element, and the default processing for a text element is to add its contents to the result tree. The text string "6.99" that makes up the list element's character data gets added to the result between the template line's curly braces.
-
The line labeled "2. Parent element" has ".." as the xsl:value-of element's select attribute value. This abbreviation tells the XSLT processor to output the contents of the list element node's parent. Its parent is prices, and with no template rule for prices or its other children in the stylesheet, the built-in rules output the character data content between that line's curly braces: the contents of the three children of the price element, complete with their carriage returns. As this shows, relying on built-in template rules to output the contents of an element that has element children leads to less control over the appearance of the children's contents.
-
The third line outputs the content of the grandparent wine element's contents using the .. abbreviation twice to say "the parent of the parent." The slash separates these two location path steps. As with line 2, the contents of this element are output using built-in template rules, resulting in a flat dump of the source tree text (including its carriage returns) to the result tree.
-
The fourth line, "4. Attribute of grandparent," uses the same XPath expression as the xsl:value-of element's select attribute in line 3, but with an addition. It has one more location path step to say "after going to the parent of the parent of the context node (../..), get the value of its grape attribute." The attribute value "Chardonnay" shows up between line 4's curly braces in the result.
-
Line 5 gets the value of a sibling by first looking to the list node's parent and then looking at its child named discounted. The discounted element's content of "5.99" shows up between line 5's curly braces in the result.
-
Line 6 looks at an uncle node (the sibling of a parent) by looking at a child of the grandparent much the same way that line 3 looked at an attribute of the grandparent: by adding a new location step (product, to look at the child element with that name) to the ../.. expression that looks at the parent of the context node's parent.
-
Line 7 uses the name() function to get the element type name of the node's parent. The template passes the .. expression for parent to the function.
-
Line 8 resembles 7 except that it passes the parent of parent XPath expression (../..) to the name() function. The element type name wine shows up between the curly braces in the result.
The pieces of these expressions mix and match well. For example, if the context node's desc uncle node has a color attribute, and you want its value when processing the context node, the xsl:value-of element's select attribute can use the expression ../../desc/@color.