Adding New Elements and Attributes
by Bob DuCharme
August 02, 2000
In the first "Transforming XML" column, we saw how an XSLT style sheet
can instruct an XSLT processing program to copy, delete, and rename elements
being copied from the input to the output. Another common task is the addition
of new elements and attributes to the output. Whether you're converting an
element to a new attribute, an attribute to a new element, or supplying either
with a function's return value or a hardcoded string, you can choose between a
quick simple way and a more complex and powerful way to add both elements and
attributes to your output.
Adding New Elements
An XSLT processor's main job is to look through a style sheet for the
various specialized elements from the XSLT namespace and to execute the
instructions specified by those elements on the tree where the input
document is stored in memory. When the processor finds elements from outside
of the XSLT namespace in any of a style sheet's templates, it passes them along
to the result tree and eventually to the output document. We call these
"literal result elements."
This makes it easy to add new elements to your output documents: simply
add elements from outside of the XSLT namespace inside of the appropriate
templates. (If you really want to output elements from the XSLT
namespace--for example, to generate style sheets as output--you'll
need XSLT's namespace-alias element.)
The following XSLT style sheet demonstrates this. When its first template
rule sees a poem element, it outputs its contents with
xsl:apply-templates and surrounds those contents with
ode tags, effectively renaming the element from
poem to ode. However, after that
ode start-tag and before the
xsl:apply-template element that shows where to put the
poem element's contents, it also
outputs two new child elements of this ode element: an
author element and a year element. The
author element has a hard-coded value of "John Milton" that
stays the same for all author elements output by this
template rule. The year element uses the
xsl:value-of element to output the value of the
poem element's year attribute.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="poem">
<ode>
<author>John Milton</author>
<year><xsl:value-of select="@year"/></year>
<xsl:apply-templates/>
</ode>
</xsl:template>
<xsl:template match="verse">
<verse><xsl:apply-templates/></verse>
</xsl:template>
</xsl:stylesheet>
The
result is the conversion of a document like the following
<poem year="1667" type="epic">
<verse line="1">Of Man's First Disobedience, and the Fruit</verse>
<verse line="2">Of that Forbidden Tree, whose mortal taste</verse>
</poem>
to this:
<?xml version="1.0" encoding="utf-8"?>
<ode><author>John Milton</author><year>1667</year>
<verse>Of Man's First Disobedience, and the Fruit</verse>
<verse>Of that Forbidden Tree, whose mortal taste</verse>
</ode>
The xsl:element element offers a more flexible way to create
new elements for your output. The following style sheet has the same effect as the previous one, but it uses
xsl:element elements instead of literal result elements. The XSLT processor looks at their name attributes to see what to call the new elements, and it then outputs the appropriate start- and end-tags for those elements.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="poem">
<ode>
<xsl:element name="author">John Milton</xsl:element>
<xsl:element name="year"><xsl:value-of select="@year"/>
</xsl:element>
<xsl:apply-templates/>
</ode>
</xsl:template>
<xsl:template match="verse">
<verse><xsl:apply-templates/></verse>
</xsl:template>
</xsl:stylesheet>
This name attribute is the key to the
advantage of xsl:element elements over literal result
elements. It offers greater flexibility, letting you create element names
dynamically by concatenating strings, calling functions, or by retrieving
element content or attribute values from elsewhere in the document. For
example, the following style sheet is similar to the one above except
that instead of converting the input poem element into an
ode element using an ode literal result
element, it uses an xsl:element element to convert it into
an element that uses the poem element's
type attribute value for a name.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="poem">
<xsl:element name="{@type}">
<author>John Milton</author>
<year><xsl:value-of select="@year"/></year>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="verse">
<verse><xsl:apply-templates/></verse>
</xsl:template>
</xsl:stylesheet>
When applied to the poem XML document above,
which has a type value of "epic" in its
poem document element, it creates an
epic element:
<?xml version="1.0" encoding="utf-8"?>
<epic><author>John Milton</author><year>1667</year>
<verse>Nine times the Space that measures Day and Night</verse>
<verse>To mortal men, he with his horrid crew</verse>
</epic>
Using this same technique for specifying element names, a template can add
elements to a result tree without even knowing an element's name in advance.
If the xsl:element element is so powerful, why bother
with literal result elements at all? Because their simplicity makes them
easier to use. To output the author and
year elements in the examples above, literal result
elements are fine.
Adding New Attributes
Adding new attributes to your output can be as simple as adding new
elements to your output with literal result elements: in the style sheet, put
them in a literal result element's start-tag.
For example, the following style sheet
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="thnad">
<widget status="done" hue="{@color}" number="{amount}" pos="{position() + 6}"/>
</xsl:template>
</xsl:stylesheet>
renames a thnad element to
widget, its color attribute to
hue, and it adds three new attributes, turning this
document
<thnad color="red">
<amount>5</amount>
</thnad>
into this:
<?xml version="1.0" encoding="utf-8"?>
<widget status="done" hue="red" number="5" pos="7"/>
The single template rule reads a thnad element and
outputs it as a widget element with four attributes that
get their values from four very different sources:
-
The first is just the hardcoded string of text "done" that will appear that way
in all widget elements that get added to the result tree.
-
The hue attribute takes its value from the
color attribute value of the input thnad
attribute.
-
The number attribute has the contents of
the thnad element's amount child as its
value.
-
The pos attribute makes a function call
and does a little math with it to create its result value: it adds 6 to the
value of the element node's position within its parent node.
Note that the template rule uses curly braces for all but the first
attribute. This tells the XSLT processor that their contents are expressions
to be evaluated and not plain text like the "done" string in the first
attribute. We call these attribute values inside of curly braces "attribute
value templates." If the second attribute specification didn't have the curly
braces and said hue="@color", that's exactly what would
have shown up in the output:
<?xml version="1.0" encoding="utf-8"?>
<widget status="done" hue="@color" number="5" pos="7"/>
Instead of putting the attribute specifications right into the
element start-tags in the style sheet, you can specify them using the
xsl:attribute element. The following style sheet does the
same thing as the last one but uses this specialized
element for each attribute specification:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="thnad">
<xsl:element name="widget">
<xsl:attribute name="status">done</xsl:attribute>
<xsl:attribute name="hue"><xsl:value-of select="@color"/></xsl:attribute>
<xsl:attribute name="number"><xsl:value-of select="amount"/></xsl:attribute>
<xsl:attribute name="pos"><xsl:value-of select="position() + 6"/></xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Like the names of the elements added to the result tree with the
xsl:element element, the names of the attributes added with
the xsl:attribute elements are specified with a
name attribute, giving you more flexibility than attributes
added as part of literal result elements like in the previous style sheet
example. For example, a name attribute value of
"xyz{position()}" in an xsl:attribute element's start-tag
tells the XSLT processor to output an attribute whose name is the string
"xyz" followed by the position value of the input element.
You can specify the value of these inserted attributes in an
xsl:attribute element either as a literal string--for
example, "done" as the value of the status attribute
above--or by generating text using the xsl:value-of
element, as with the other attribute values in the example.
To show which element these attributes belong to, the
xsl:attribute elements are inside of the
xsl:element element that adds a widget
element to the result tree when the XSLT processor finds a thnad
element in the input. As children of an xsl:stylesheet
element, an xsl:attribute wouldn't make any sense, because
there would be no way to figure out what element type the attribute belonged
to.
If you need to re-use a group of attributes in multiple elements of your
output (for example, formatting instructions that apply to several HTML
element types upon output), you can store them in an
xsl:attribute-set element and then reference the collection
with the use-attribute-sets attribute of
xsl:element.
For example, the following shows a group of
xsl:attribute elements in an
xsl:attribute-set element named "widgetAttrs" and an
xsl:element element that incorporates those attributes with
a value of "widgetAttrs" for its use-attribute-sets
attribute. Note how this attribute name is in the plural; the value can list
more than one attribute set as long as spaces separate the names and all the
names represent existing xsl:attribute-set elements.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:attribute-set name="widgetAttrs">
<xsl:attribute name="status">done</xsl:attribute>
<xsl:attribute name="hue"><xsl:value-of select="@color"/></xsl:attribute>
<xsl:attribute name="number"><xsl:value-of select="amount"/></xsl:attribute>
<xsl:attribute name="pos"><xsl:value-of select="position() + 6"/></xsl:attribute>
</xsl:attribute-set>
<xsl:template match="thnad">
<xsl:element name="widget" use-attribute-sets="widgetAttrs">
<xsl:attribute name="prodmgr">BD</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
If an xsl:element element names some attributes
with an xsl:use-attribute-sets attribute, it can still use
the xsl:attribute element to add more, just as the
xsl:template example above adds a
"prodmgr" attribute to the
"widgetAttrs" set referenced by its template.
We've seen that there are simple, easy ways to add elements and
attributes to your output as well as more complex, powerful alternatives. Some
of the demonstrations of the more powerful approaches took advantage of other
XSLT features not explained here, but we'll examine their use more closely in
future "Transforming XML" columns.