Creating XML Elements at Runtime with XSLT
Extensible Markup Language (XML) documents are made up of XML elements, and you create XML elements with an opening tag, such as <DOCUMENT>, followed by any element content, such as text or other elements, and ending with the matching closing tag that starts with </, such as </DOCUMENT>. You enclose the entire document, except for processing instructions, in one element, called the root element. However, it's not always possible to know the names of the new elements you want to create. You can cobble together elements created on the fly, treating them as raw text, but that's fairly crude because it treats markup as text. This is where Extensible Stylesheet Language Transformation (XSLT) comes in.
To illustrate how to create these runtime elements with XSLT, I'll be using an XML document called planets.xml that stores data about the planets Mercury, Venus, and Earth, such as their mass, length of their day, density, distance from the sun, and so on.
The <xsl:element> Element: Creating New Elements at Runtime
The <xsl:element> element is very useful if you need to determine a new element's name at runtime. Here are the three attributes of this element:
name (mandatory): Name of the element you want to create. Set to an attribute value template that returns a QName.
namespace (optional): The namespace uniform resource identifier (URI) of the new element. Set to an attribute value template returning a URI.
use-attribute-sets (optional): Specifies the attribute sets containing the attributes for this element. Set to a whitespace-separated list of QNames.
The <xsl:element> element contains a template body.
For example, say that I want to store the names of the planets in NAME attributes rather than in a <NAME> element in planets.xml, like the following code.
NOTE
Note that breaks in the middle of a code element might happen because of spacing limitations on this page; simply continue typing so that the tag stays on the previous line, as with <!--At perihelion--> shown next. You'll notice that the continued line often starts all the way over at the left margin without the indentation used in the line preceding it.
<?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> . . .
Suppose now that I want to use the value of that attribute to create new element names in the result document, such as <Mercury>, <Venus>, and <Earth>:
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <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--> </Mercury> . . .
In this case, I don't know the name of the output element until runtime, so I can't just use a literal result element. I could cobble together a new element, treating it as text this way, where I'm using the <xsl:text> element's disable-output-escaping attribute to output characters such as "<":
<?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="PLANET"> <xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="@NAME"/> <xsl:text disable-output-escaping="yes">></xsl:text> <xsl:apply-templates/> <xsl:text disable-output-escaping="yes"></</xsl:text> <xsl:value-of select="@NAME"/> <xsl:text disable-output-escaping="yes">></xsl:text> </xsl:template> </xsl:stylesheet>
But this method is clumsy and it treats markup as simple text. On the other hand, I could create a new element using the name of a planet with <xsl:element>, getting the name of the new planet from the name attribute, as I've done in Listing 1.
Listing 1: Using the <xsl:element> 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="PLANET"> <xsl:element name="{@NAME}"> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet>
This is a lot cleaner and simpler. Listing 2 shows the result, where I've created the new elements, each with the name of a different planet and created at runtime.
Listing 2: The Result of Using <xsl:element>
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xml" href="planets.xsl"?> <PLANETS> <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--> </Mercury> <Venus> <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--> </Venus> <Earth> <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--> </Earth> </PLANETS>
This article has shown you just one way that XSLT transformations can make working with XML documents much simpler and more efficient. Using the method I explained here, you can create new elements and name them when the XSLT transformation takes place.