Altering Document Structure Based on Input
So far, the templates in this chapter have been fairly rigid skeletons, specifying exactly what should go into the output document in what order. But you can use XSLT elements such as <xsl:element>, <xsl:attribute>, <xsl:text>, and so on to create new nodes on the fly, based on what you find in the input document. Ill take a look at how this works now.
Creating Attribute Templates
Say that you wanted to convert the text in some elements to attributes in other elementshow could you do it? Attribute values must be quoted in XML, but you cant just use expressions like these, where Im taking the values of <NAME>, <MASS>, and <DAY> elements and trying to make them into attribute values:
<xsl:template match="PLANET"> <PLANET NAME="<xsl:value-of select=NAME/>" MASS="<xsl:value-of select=MASS/>" DAY="<xsl:value-of select=DAY/>" />
This wont work because you cant use < inside attribute values, as I have here. Instead, you must use an expression like {NAME}; heres the proper XSLT:
<?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <PLANET NAME="{NAME}" MASS="{MASS}" DAY="{DAY}" /> </xsl:template> </xsl:stylesheet>
Heres the resulting documentnote that Ive been able to convert the values in various elements to attributes:
<HTML> <HEAD> <TITLE> Planets </TITLE> </HEAD> <BODY> <PLANET DAY="58.65" MASS=".0553" NAME="Mercury"> </PLANET> <PLANET DAY="116.75" MASS=".815" NAME="Venus"> </PLANET> <PLANET DAY="1" MASS="1" NAME="Earth"> </PLANET> </BODY> </HTML>
You can even include multiple expressions in curly braces, like this, where Im adding the units for mass and day measurements from the UNITS attribute in the original elements:
<?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <PLANET NAME="{NAME}" MASS="{MASS} {MASS/@UNITS}" DAY="{DAY} {DAY/@UNITS}" /> </xsl:template> </xsl:stylesheet>
Creating New Elements
You can create new elements with the <xsl:element> element. For example, say that I store the name of planets in a NAME attribute instead of a <NAME> element in planets.xml, like this:
<?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <PLANET NAME="Mercury"> <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> . . .
I could create a new element using the name of the planet with <xsl:element>, supplying the name of the new planet with the name attribute, and enclosing a <MASS> element this way:
<?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:element name="{@NAME}"> <MASS><xsl:value-of select="MASS"/></MASS> </xsl:element> </xsl:template> </xsl:stylesheet>
Here is the result, where Ive created a new <mercury> element:
<HTML> <HEAD> <TITLE> Planets </TITLE> </HEAD> <BODY> <Mercury> <MASS>.0553</MASS> </Mercury> . . .
In this way, you can create new elements and name them when the XSLT transformation takes place.
Creating New Attributes
Just as you can create new elements with <xsl:element> and set the element name and content under programmatic control, you can do the same for attributes using the <xsl:attribute> element.
Heres an example; in this case, Im creating new <PLANET> elements with attributes corresponding to the various planet names, and values taken from the COLOR attribute in the original <PLANET> elements:
<?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> Planets </TITLE> </HEAD> <BODY> <xsl:apply-templates select="PLANET"/> </BODY> </HTML> </xsl:template> <xsl:template match="PLANET"> <PLANET> <xsl:attribute name="{NAME}"> <xsl:value-of select="@COLOR"/> </xsl:attribute> </PLANET> </xsl:template> </xsl:stylesheet>
Here are the results; as you can see, Ive created new attributes on the fly, using the names of the planets:
<HTML> <HEAD> <TITLE> Planets </TITLE> </HEAD> <BODY> <PLANET Mercury="RED"> </PLANET> <PLANET Venus="WHITE"> </PLANET> <PLANET Earth="BLUE"> </PLANET> </BODY> </HTML>