
Generating Unique IDs and Linking to Them
by Bob DuCharme
October 03, 2001
When an XSLT stylesheet converts one XML document into another, the
ability to add unique ID values to elements in the result document can
make the result document much more useful to applications that use
it. Adding unique IDs can, for example, turn each element into the
unique target of a link.
XSLT's generate-id() function generates a unique ID for a
node passed to it as an argument. This ID starts with a letter so that
you can use it as the value of an XML ID attribute. For example, the
following stylesheet copies an XML document and adds a uid
("unique ID") attribute to each chapter, sect1, and
sect2 element. The xsl:value-of instruction uses the
generate-id() function in the stylesheet's first template
rule to create a value for these attributes.
<xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:outputmethod="xml"omit-xml-declaration="yes"/>
<xsl:templatematch="chapter|sect1|sect2">
<xsl:copy>
<xsl:attributename="uid">
<xsl:value-ofselect="generate-id(.)"/>
</xsl:attribute>
<xsl:apply-templatesselect="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:templatematch="@*|node()">
<xsl:copy>
<xsl:apply-templatesselect="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The stylesheet turns this XML document
<chapter>
<para>Thenwithexpandedwingshesteershisflight</para>
<figure><title>"IncumbentontheDuskyAir"</title>
<graphicfileref="pic1.jpg"/></figure>
<para>Aloft,incumbentontheduskyAir</para>
<sect1>
<para>Thatfeltunusualweight,tillondryLand</para>
<figure><title>"HeLights"</title>
<graphicfileref="pic2.jpg"/></figure>
<para>Helights,ifitwereLandthateverburned</para>
<sect2>
<para>Withsolid,astheLakewithliquidfire</para>
<figure><title>"TheLakewithLiquidFire"</title>
<graphicfileref="pic3.jpg"/></figure>
</sect2>
</sect1>
</chapter>
into this one:
<chapteruid="N134711680">
<para>Thenwithexpandedwingshesteershisflight</para>
<figure><title>"IncumbentontheDuskyAir"</title>
<graphicfileref="pic1.jpg"/></figure>
<para>Aloft,incumbentontheduskyAir</para>
<sect1uid="N134683456">
<para>Thatfeltunusualweight,tillondryLand</para>
<figure><title>"HeLights"</title>
<graphicfileref="pic2.jpg"/></figure>
<para>Helights,ifitwereLandthateverburned</para>
<sect2uid="N134684064">
<para>Withsolid,astheLakewithliquidfire</para>
<figure><title>"TheLakewithLiquidFire"</title>
<graphicfileref="pic3.jpg"/></figure>
</sect2>
</sect1>
</chapter>
Do you use generate-id() differently than Bob DuCharme explains in this article? Share your experience in our forums.
Post your comments
Your XSLT processor may generate different values with the
generate-id() function. In fact, if you run the same
stylesheet with the same input document a second time, the XSLT
processor may not generate the same ID values that it generated the
first time. However, if you call generate-id() more than once
in one run with the same node as an argument, it generates the same ID
value each time for that node. Because unique IDs are popular ways to
identify link destinations, this consistency of the
generate-id() function makes it a great way to generate
links.
For example, we saw in an earlier
"Transforming XML" column how to copy a document like the one
above, adding a list of all of its illustrations at the beginning of
the result document. If we make the result tree version an HTML file,
we can use the generate-id function to turn each entry of
this opening illustration list into an HTML link to the img
element in the body of the document that has the illustration:
<xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:outputmethod="html"/>
<xsl:templatematch="chapter">
<html><body>
<!--Generatealistofpicturetitles,witheach
titlelinkingtothepictureinthepoembelow.-->
<b>Pictures:</b><br/>
<xsl:for-eachselect="descendant::figure">
<ahref="#{generate-id(graphic)}">
<xsl:value-ofselect="title"/></a><br/>
</xsl:for-each>
<xsl:apply-templates/>
</body></html>
</xsl:template>
<xsl:templatematch="para">
<p><xsl:apply-templates/></p>
</xsl:template>
<xsl:templatematch="graphic">
<!--Imageandtitleascaption,centered.-->
<center><aname="{generate-id(.)}"><imgsrc="{@fileref}"/></a>
<b><xsl:value-ofselect="../title"/></b></center>
</xsl:template>
<!--Suppressfiguretitlebecause"graphic"template
rulealreadyaddedittoresulttree.-->
<xsl:templatematch="figure/title"/>
</xsl:stylesheet>
With the source document above, this stylesheet creates the
following HTML document:
<html>
<body>
<b>Pictures:</b>
<br>
<ahref="#N134691840">"IncumbentontheDuskyAir"</a>
<br>
<ahref="#N134692416">"HeLights"</a>
<br>
<ahref="#N134757920">"TheLakewithLiquidFire"</a>
<br>
<p>Thenwithexpandedwingshesteershisflight</p>
<center>
<aname="N134691840"><imgsrc="pic1.jpg"></a>
<b>"IncumbentontheDuskyAir"</b>
</center>
<p>Aloft,incumbentontheduskyAir</p>
<p>Thatfeltunusualweight,tillondryLand</p>
<center>
<aname="N134692416"><imgsrc="pic2.jpg"></a>
<b>"HeLights"</b>
</center>
<p>Helights,ifitwereLandthateverburned</p>
<p>Withsolid,astheLakewithliquidfire</p>
<center>
<aname="N134757920"><imgsrc="pic3.jpg"></a>
<b>"TheLakewithLiquidFire"</b>
</center>
</body>
</html>
(To view the HTML document, you'll need to supply your own
pic1.jpg, pic2.jpg, and pic3.jpg files.)
The stylesheet uses the generate-id() ID twice:
Also in Transforming XML
Automating Stylesheet Creation
Appreciating Libxslt
Push, Pull, Next!
Seeking Equality
The Path of Control
-
As the xsl:for-each
instruction in the "chapter" template rule adds each figure
element's title to the result tree for the "Pictures:" list
at the beginning of the result document, it puts each of these
title elements inside of an HTML a element to link
to the appropriate picture in the main part of the document. Each of
these a elements has an href attribute to indicate
the link destination. An href attribute that begins with a
pound sign ("#") looks for the link destination in the same document
-- specifically, it looks for another a element with a
name attribute value equal to the part after the pound sign
in the link origin. For example, an a start-tag of
<ahref="#a123"> links to an a element with an
<a name="a123"> start-tag elsewhere in the same
document.
Instead of the string "a123" identifying each link destination,
this stylesheet uses the generate-id() function to make up an
identifying string. Because the graphic element node is
passed to it as an argument, the function creates an ID string for
each of the three graphic elements: "N134691840",
"N134692416", and "N134757920".
To create the link destinations,
the "graphic" template rule puts each HTML img element in the
result tree inside of an a element. These img
elements use the value of the source tree graphic elements'
fileref attributes as their src value, and the
a elements use the generate-id() function to create
the values for their name attributes. Passing this function
an argument of "." is the same as passing it
self::node(), which in this case means passing it the
graphic element node, so the XSLT processor generates an ID
value for each graphic node. These are the same three nodes
that the earlier use of the generate-id() created IDs for,
and it creates the same three values: "N134691840", "N134692416", and
"N134757920". When this HTML file is displayed in a browser, each link
in the opening "Pictures:" list will now go to the corresponding
picture in the document.
This consistency in the generate-id() function's treatment
of a particular node, even if the function generates an ID for that
node more than once, is the key to its power. These graphic
elements didn't have IDs in the source document; with the help of this
function, their equivalent in the result document has them, and other
elements in that document use those IDs to link to them.