Kirk Evans Blog

.NET From a Markup Perspective

Using autoincrement for an XML document

On the GotDotNet.com message boards, GeFIN asks how to create a unique ID using XML.

There are several ways to accomplish this. For instance, you could use the classes in the System.Xml namespace to load the document, select the nodes from the document, sort them, then get the value of the last node in the node list. You might simply generate a Guid for each node and use that new value. You could retrieve a value from a database, using something like Oracle’s sequences, or you could use SQL Server’s XML capabilities to shred the document and handle the autoincrement the IDs internally.

Let’s look at some ways that we can leverage XSLT to accomplish this task.

The first (and likely simplest) means of generating IDs is to use XSLT’s generate-id() function to append arbitrary unique IDs to each node. We use the XSLT identity transformation:

<xsl:transform version =”1.0 xmlns:xsl =”http://www.w3.org/1999/XSL/Transform >
<xsl:template match =”node() | @* >
<xsl:copy>
 <xsl:apply-templates select =”@*|node() />
 </xsl:copy>
 </xsl:template>
<xsl:template match =”bar >
<xsl:copy>
<xsl:attribute name =”genID >
 <xsl:value-of select =”generate-id(.) />
 </xsl:attribute>
 <xsl:apply-templates select =”@*|node() />
 </xsl:copy>
 </xsl:template>
 </xsl:transform>

Given the instance document:

<foo>
 <bar id =”1 />
 <bar id =”2 />
 <bar id =”3 />
 <bar id =”8 />
 <bar id =”4 />
 <bar id =”5 />
 <bar id =”6 />
 <bar id =”7 />
 </foo>

The output result is:

<foo>
 <bar genID =”IDABLWX id =”1 />
 <bar genID =”IDADLWX id =”2 />
 <bar genID =”IDAFLWX id =”3 />
 <bar genID =”IDAHLWX id =”8 />
 <bar genID =”IDAJLWX id =”4 />
 <bar genID =”IDALLWX id =”5 />
 <bar genID =”IDANLWX id =”6 />
 <bar genID =”IDAPLWX id =”7 />
 </foo>

In the above example, we generated unique IDs for each node. Further runs of this transformation might provide different IDs for each node, which might not be acceptable. In GeFIN’s case, he actually desired the ability to use some type of an autoincrement. Using an autoincrement naturally assumes knowledge of the last maximum value used. While XSLT does not provide a means to find a maximum value, this problem has already been addressed in several ways. EXSLT provides a max function to get a max value from a nodeset. Dimitre Novatchev’s FXSL also provides a simple means of retrieving a maximum value. Let’s look at developing our own solution using a recursive named template that implements a recursive quick sort algorithm.

<xsl:stylesheetxmlns:xsl=”http://www.w3.org/1999/XSL/Transform version =”1.0 >
 <xsl:output method =”text />
<xsl:template name =”max >
 <xsl:param name =”list />
<xsl:choose>
<xsl:when test =”$list >
 <xsl:variable name =”first select =”$list[1]/@id />
<xsl:variable name =”max-of-rest >
<xsl:call-template name =”max >
 <xsl:with-param name =”list select =”$list[position()!=1] />
 </xsl:call-template>
 </xsl:variable>
<xsl:choose>
<xsl:when test =”$first > $max-of-rest >
 <xsl:value-of select =”$first />
 </xsl:when>
<xsl:otherwise>
 <xsl:value-of select =”$max-of-rest />
 </xsl:otherwise>
 </xsl:choose>
 </xsl:when>
 <xsl:otherwise>0</xsl:otherwise>
 </xsl:choose>
 </xsl:template>
<xsl:template match =”/ >
<xsl:variable name =”maxVal >
<xsl:call-template name =”max >
 <xsl:with-param name =”list select =”/foo/bar />
 </xsl:call-template>
 </xsl:variable>
 <xsl:text>The max value is:</xsl:text>
 <xsl:value-of select =”$maxVal />
 <xsl:text> The next value is:</xsl:text>
 <xsl:value-of select =”$maxVal + 1 />
 </xsl:template>
 </xsl:stylesheet>

The result is:

The max value is:8
The next value is:9