Axes make up the first part of step patterns. For example, in the step pattern child:: NAME , which refers to a <NAME> element that is a child of the context node, child is called the axis. Patterns support two axes (XPath, on the other hand, supports no less than 13 different axessee Chapter 7):
The attribute axis holds the attributes of the context node.
The child axis holds the children of the context node. The child axis is the default axis if one is not explicitly set.
You can use axes to specify a location step or path as in the following example, where I use the child axis to indicate that I want to match the child nodes of the context node, which is a <PLANET> element:
<xsl:template match="PLANET"> <HTML> <CENTER> <xsl:value-of select="child::NAME"/> </CENTER> <CENTER> <xsl:value-of select="child::MASS"/> </CENTER> <CENTER> <xsl:value-of select="child::DAY"/> </CENTER> </HTML> </xsl:template>
Look at the following examples that use axes:
child::PLANET . Returns the <PLANET> element children of the context node.
child::* . Returns all element children (* matches only elements) of the context node.
attribute::UNIT . Returns the UNITS attribute of the context node.
child::*/child::PLANET . Returns all <PLANET> grandchildren of the context node.
Although these examples make it seem that you can use only the child and attribute axes, in practice this is not quite so. When it comes to specifying children, the child axis is a little limited, because you must specify every level that you want to match, such as "child::PLANETS/child::PLANET/child::MASS" , which matches a <MASS> element that is a child of a <PLANET> element that is a child of the <PLANETS> element. If you want to match all <MASS> elements that appear anywhere in the <PLANETS> element, whether they are children, grandchildren, great-grandchildren, and so on, it looks as if theres no way to do that in one pattern. In XPath, you can do that with an expression like "child::PLANETS/descendant::MASS" , but you cant use the descendant axis in patterns. However, remember that you can use the // operator, which amounts to the same thing. For example, the pattern "child::PLANETS//child::MASS" matches all <MASS> elements anywhere inside the <PLANETS> element. (In fact, this is a minor inconsistency in the specification.)
The next example shows how I might put this pattern to work, replacing the text in all <MASS> elements, no matter where they are inside the <PLANETS> element, with the text "Very heavy!" . To copy over all the other nodes in planets.xml to the XML result document, I also set up a rule that matches any node using the node node test, which youll see later. Note that although the pattern that matches any node also matches all the <MASS> elements, the "child::PLANETS//child::MASS" pattern is a much more specific match, which (as discussed in Chapter 3) means the XSLT processor gives it higher priority for <MASS> elements:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="@*node()"> <xsl:copy> <xsl:apply-templates select="@*node()"/> </xsl:copy> </xsl:template> <xsl:template match="child::PLANETS//child::MASS"> <MASS> Very heavy! </MASS> </xsl:template> </xsl:stylesheet>
And heres the resulting XML document:
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET> <NAME>Mercury</NAME> <MASS> Very heavy! </MASS> <DAY UNITS="days">58.65</DAY> <RADIUS UNITS="miles">1516</RADIUS> <DENSITY UNITS="(Earth = 1)">.983</DENSITY> <DISTANCE UNITS="million miles">43.4</DISTANCE><!--At perihelion--> </PLANET> <PLANET> <NAME>Venus</NAME> <MASS> Very heavy! </MASS> <DAY UNITS="days">116.75</DAY> <RADIUS UNITS="miles">3716</RADIUS> <DENSITY UNITS="(Earth = 1)">.943</DENSITY> <DISTANCE UNITS="million miles">66.8</DISTANCE><!--At perihelion--> </PLANET> <PLANET> <NAME>Earth</NAME> <MASS> Very heavy! </MASS> <DAY UNITS="days">1</DAY> <RADIUS UNITS="miles">2107</RADIUS> <DENSITY UNITS="(Earth = 1)">1</DENSITY> <DISTANCE UNITS="million miles">128.4</DISTANCE><!--At perihelion--> </PLANET> </PLANETS>
You can also take advantage of a number of abbreviations when specifying axes in patterns. These abbreviations are almost invariably used when youre specifying axes in patterns.
There are two rules for abbreviating axes in patterns:
child::childname can be abbreviated as childname .
attribute::childname can be abbreviated as @ childname .
The following list includes some examples of patterns using abbreviated syntaxyoull see a lot more at the end of the chapter:
PLANET . Matches the <PLANET> element children of the context node.
* . Matches all element children of the context node.
@UNITS . Matches the UNITS attribute of the context node.
@* . Matches all the attributes of the context node.
*/PLANET . Matches all <PLANET> grandchildren of the context node.
//PLANET . Matches all <PLANET> descendants of the document root.
PLANETS//PLANET . Matches all <PLANET> element descendants of the <PLANETS> element children of the context node.
//PLANET/NAME . Matches all the <NAME> elements that are children of a <PLANET> parent.
PLANET[NAME] . Matches the <PLANET> children of the context node that have <NAME> children.
In a pattern such as "child::PLANET" , "child" is the axis and "PLANET" is the node test , which is the second part of step patterns.