In article <11**********************@k70g2000cwa.googlegroups .com>,
<aj****@blueyonder.co.ukwrote:
>I am trying to allocate a unique ID to every instance of tag 'foo' in a
large XML document. currently I'm doing this:
<xsl:variable name="UniqueId">
<xsl:number count="foo" level="any"/>
</xsl:variable>
The only reasonably efficient XSLT solution I can come up with is a 3
step process. First assign ids (which won't be numbers, and could be
in any format) to the elements using generate-id(). Then generate a
file mapping ids to sequence numbers (using position() on a node-set
containing all the desired elements). Then use key() to look up the
sequence numbers in the map file. I think this should be order N (or
close) for most stylesheet processors.
Here are the stylesheets, which assume that you want to operate on all
"foo" elements, that you want to call the sequence number attribute
"seq", and that you don't already have attributes called "id".
(1) Assign ids:
<xsl:stylesheet 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="foo">
<xsl:copy>
<xsl:attribute name="id"><xsl:value-of select="generate-id()"/></xsl:attrib\
ute>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
(2) Create map file from the result of step 1:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<junk>
<xsl:apply-templates select="//foo"/>
</junk>
</xsl:template>
<xsl:template match="foo">
<map id="{@id}" seq="{position()}"/>
</xsl:template>
</xsl:stylesheet>
(3) Map ids to sequence numbers (pass in the URL of the map file as the
"mapfile" parameter, and use the file generated in step 1 as the input):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="mapfile"/>
<xsl:key name="id" match="map" use="@id"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="foo">
<xsl:copy>
<xsl:apply-templates select="@*[name() != 'id']"/>
<xsl:attribute name="seq">
<xsl:variable name="id" select="@id"/>
<!-- the for-each is just to set the context node for key() -->
<xsl:for-each select="document($mapfile)">
<xsl:value-of select="key('id', $id)/@seq"/>
</xsl:for-each>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
-- Richard
--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.