XSLT, id values

ID Attributes

1. Generating same IDs across invocations
2. ID attributes
3. Using keys instead of ID values
4. Unique values across multiple documents
5. Mixing generate-id and existing id values

1.

Generating same IDs across invocations

Jeni Tennison

How to do this really depends on whether and in what way your source document is going to change across invocations. A simple approach is to generate an id that uniquely identifies the element within the document by numbering all the elements of a certain type:

<xsl:attribute name="id">
  <xsl:value-of select="name()">-<xsl:number level="any" />
</xsl:attribute>

will generate para-1, para-2, para-3 and so on throughout the document. The xsl:number with level="any" numbers instances of the element across the entire document. This is enough if your source document is not going to change at all across invocations.

Otherwise you need something a bit more complex, and that relies on asking the question: what is it that uniquely identifies this element? It could be its content, the combination of values of attributes, its position, or anything. Once you've answered that question, you can use the answer to generate the unique id.

2.

ID attributes

Chris Maden

Q expansion: I have an XML instance that makes use of the ID/IDREF attributes to establish links within the document, If I use "id($targetid)" it does not work.

For this to work, the ID attributes must be declared as such, and the processor must read those declarations. If you're using XT, for example, you need to have the declarations in the DTD.

3.

Using keys instead of ID values

Joerg Pietschmann

The id() function searches the whole tree. But there must exist a DTD for this tree, which declares the attribute ID as ID ( See http://www.xmlfiles.com/dtd/dtd_attributes.asp).

If you don't want to have a DTD, you can set up a key

<xsl:key name="nodes" match="node" use="@ID"/>

as top-level.

And later in a template

<xsl:apply-templates select="key('nodes', 'abc')"/>

which applies templates to all <node>'s with a value of ID attribute of 'abc'.

4.

Unique values across multiple documents

Dimitre Novatchev.


> > I want to create an index across multiple XML
> > documents in order to check the name uniqueness of the
> > element "Member".
> >
>
> I think that to check uniqueness across multiple documents in 1.0, the
> only way is to copy the relevant data into a new temporary document, and
> then use the standard grouping (or deduplicating) techniques on that.

Not really. This can be achieved using the "makeDistinct" template from FXSL without creating a new temporary document.

The solution was first described on this list almost two years ago: http://sources.redhat.com/ml/xsl-list/2001-12/msg00806.html

5.

Mixing generate-id and existing id values

David Tolpin



> generate-id() is not guaranteed to generate an identifier unique 
> for the document.
> It is only guaranteed to produce a different string for each node. 
> If another node already
> has the same id as one just generated, there will be 
> duplicate identifiers in the result.

Here is one that will do the job

<xsl:transform 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
	version="1.0">

  <xsl:key name="ids" match="//*" use="@id"/>

  <xsl:template match="*">
    <xsl:copy>
      <xsl:if test="not(@id)">
	<xsl:attribute name="id">
	  <xsl:call-template name="generate-unique-id">
	    <xsl:with-param name="prefix" select="generate-id()"/>
          </xsl:call-template>
	</xsl:attribute>
      </xsl:if>
      <xsl:apply-templates select="*|@*|text()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="generate-unique-id">
    <xsl:param name="prefix"/>
    <xsl:param name="suffix"></xsl:param>
    <xsl:variable name="id" select="concat($prefix,$suffix)"/>

    <xsl:choose>
      <xsl:when test="key('ids',$id)">
        <xsl:call-template name="generate-unique-id">
	  <xsl:with-param name="prefix" select="$prefix"/>
	  <xsl:with-param name="suffix" select="concat($suffix,'x')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$id"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="@*|text()">
    <xsl:copy/>
  </xsl:template>

</xsl:transform>