Inside Xslt

Pattern Examples

The best way to understand patterns is by example. Suppose that you want to transform planets.xml into planets.html, but retain only the first planet, Mercury. You can do that with the predicate [position() < 2] , because the first planets position is 1, the next is 2, and so on. Note, however, that < is invariably a sensitive character for XSLT processors, because its what you use to start markup; rather than <, you should use &lt.; . And note that you have to strip the other elements out of planets.xml by supplying an empty template for them, which I can do with the predicate [position() >= 2] :

Listing 4.10 Retaining Only Mercury

<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET[position() &lt.; 2]"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="PLANET[position() >= 2]"> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet>

Heres the resulting documentnote that only the first planet, Mercury, was retained:

<HTML> <HEAD> <TITLE> The Planets Table </TITLE> </HEAD> <BODY> <H1> The Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Mercury</TD> <TD>.0553 (Earth = 1)</TD> <TD>1516 miles</TD> <TD>58.65 days</TD> </TR> </TABLE> </BODY> </HTML>

In the following example, COLOR and POPULATED attributes have been added to Earths <PLANET> element:

<PLANET COLOR="blue" POPULATED="yes"> <NAME>Earth</NAME> <MASS UNITS="(Earth = 1)">1</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>

How can you select only elements that have both COLOR and POPULATED attributes? You can use the predicate "[@COLOR and @POPULATED]" . To strip out the other elements so the default rules dont place their text into the result document, you can use a predicate such as "[not(@COLOR) or not(@POPULATED)]" , as shown in Listing 4.11.

Listing 4.11 Selecting Only Elements That Have Both COLOR and POPULATED Attributes

<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> Colorful, Populated Planets </TITLE> </HEAD> <BODY> <H1> Colorful, Populated Planets </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET[@COLOR and @POPULATED]"> <TR> <TD><xsl:value-of select="NAME"/></TD> <TD><xsl:apply-templates select="MASS"/></TD> <TD><xsl:apply-templates select="RADIUS"/></TD> <TD><xsl:apply-templates select="DAY"/></TD> </TR> </xsl:template> <xsl:template match="PLANET[not(@COLOR) or not(@POPULATED)]"> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="RADIUS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet>

And heres the result:

<HTML> <HEAD> <TITLE> Colorful, Populated Planets </TITLE> </HEAD> <BODY> <H1> Colorful, Populated Planets </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD>Earth</TD> <TD>1 (Earth = 1)</TD> <TD>2107 miles</TD> <TD>1 days</TD </TR> </TABLE> </BODY> </HTML>

You can see this document in Figure 4.3.

Figure 4.3. Using XPath predicates to check attributes.

In the following example, I copy planets.xml to a new XML document, and change the text in Venuss <NAME> element to "The Planet of Love" . To do that, I start by copying all nodes and attributes to the result document:

<?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> . . .

Now Ill add a new rule that matches <NAME> elements that have the text Venus with the pattern "NAME[text() = 'Venus']" . Even though <NAME> elements match both rules in this stylesheet, the rule with the pattern "NAME[text() = 'Venus']" is a more specific match, so the XSLT processor uses it for Venuss <NAME> element:

<?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="NAME[text() = 'Venus']"> <NAME> The Planet of Love </NAME> </xsl:template> </xsl:stylesheet>

And heres the result:

<?xml version="1.0" encoding="utf-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET> <NAME>Mercury</NAME> <MASS UNITS="(Earth = 1)">.0553</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> The Planet of Love </NAME> <MASS UNITS="(Earth = 1)">.815</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 UNITS="(Earth = 1)">1</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>

In fact, in XPath expressions, you can refer to the context node as ., and the default value of a node is its text, so the following rule works exactly the same way:

<?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="NAME[. = 'Venus']"> <NAME> The Planet of Love </NAME> </xsl:template> </xsl:stylesheet>

Its worth packing in as many examples as possibleyou can never have too many match pattern or XPath examples. The following is a good collection of match pattern examples:

That completes this coverage of match patterns for the moment; youll see related material in Chapter 7 on XPath expressions. Chapter 5 starts looking at ways of working with the data in XML documents by sorting that data and using it to make choices based on data values.

Категории