xsl-list
[Top] [All Lists]

generate-id for identical elements

2004-09-23 10:13:37
Hi,

I apologize for the length of the message. I have included XML, XSL, and 
HTML snippets, so it's rather long. I also apologize for posting from 
Lotus Notes, but I don't have and can't get a different e-mail program 
here at my client site (the IT department frowns on anything but Notes). I 
hope using Notes won't cause too much trouble.

To summarize the problem, I have identical elements (but they do have 
different parent elements), and I am trying to get cross-references to 
these identical elements with generate-id(). I've solved the problem 
without generate-id() by concatenating a unique identifier from the titles 
of the elements (child and parent). I've also solved the problem of 
cross-references with a key and generate-id() - that's the solution I use 
for production documents at present. However, that system does not allow 
two elements to have identical titles. I would like to remove that 
limitation and still use generate-id() - rather than having to key in a 
long identifier for each cross-reference.

The following XML structure represents the problem:

<book>
  <chapter title="Chapter 1">
    <paragraph>The remainder of this book contains the following 
material:</paragraph>
    <list type="bulleted">
      <listitem><ref target="chapter|Chapter 2">Chapter 2</ref>
        <list type="bulleted">
          <listitem><ref target="chapter|Chapter 
2/topic|Introduction">Introduction</ref></listitem>
        </list>
      </listitem>
      <listitem><ref target="chapter|Chapter 3">Chapter 3</ref>
        <list type="bulleted">
          <listitem><ref target="chapter|Chapter 
3/topic|Introduction">Introduction</ref></listitem>
        </list>
      </listitem>
    </list>
  </chapter>
  <chapter title="Chapter 2">
    <topic title="Introduction"/>
  </chapter>
  <chapter title="Chapter 3">
    <topic title="Introduction"/>
  </chapter>
</book>

As you can see, some elements (<topic title="Introduction"/>) are 
identical. However, they are the children of non-identical elements 
(<chapter title="Chapter 2"/> and <chapter title="Chapter 2/">).

I've tried all kinds of things, but I can't get generate-id() to give me 
the same path that it assigns to the referenced element. Just so you'll 
have something to work with and get an idea of my thinking (wrong as it 
is), here's the current stylesheet (representing the last of MANY failed 
ideas) :

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates/>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="chapter">
    <h1><a name="{generate-id()}"/><xsl:value-of select="@title"/></h1>
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="list">
    <ul>
      <xsl:apply-templates/>
    </ul>
  </xsl:template>

  <xsl:template match="listitem">
    <li><xsl:apply-templates/></li>
  </xsl:template>

  <xsl:template match="paragraph">
    <p><xsl:apply-templates/></p>
  </xsl:template>

  <xsl:template match="ref">
    <xsl:variable name="reftext">
      <xsl:call-template name="makeRef">
        <xsl:with-param name="inString" select="@target"/>
      </xsl:call-template>
    </xsl:variable>
    <a href="#{generate-id(//$reftext)}"><xsl:value-of select="."/></a>
  </xsl:template>

  <xsl:template match="topic">
    <h3><a name="{generate-id()}"><xsl:value-of select="@title"/></a></h3>
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template name="makeRef">
    <xsl:param name="inString"/>
    <xsl:choose>
      <xsl:when test="contains($inString, '/')">
        <xsl:variable name="thisPart" select="substring-before($inString, 
'/')"/>
        <xsl:value-of 
select="substring-before($thisPart,'|')"/>[(_at_)title='<xsl:value-of 
select="substring-after($thisPart, '|')"/>']/
        <xsl:call-template name="makeRef">
          <xsl:with-param name="inString" 
select="substring-after($inString, '/')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of 
select="substring-before($inString,'|')"/>[(_at_)title='<xsl:value-of 
select="substring-after($inString, '|')"/>']
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Here's the output from Saxon 8 (with a little hand formatting for clarity 
and brevity):

<html>
  <body>
    <h1><a name="d1e3"></a>Chapter 1</h1>
      <p>The remainder of this book contains the following material:</p>
      <ul>
        <li><a href="#d2">Chapter 2</a>
          <ul>
            <li><a href="#d3">Introduction</a></li>
          </ul>
        </li>
        <li><a href="#d4">Chapter 3</a>
          <ul>
            <li><a href="#d5">Introduction</a></li>
          </ul>
        </li>
      </ul>
    <h1><a name="d1e36"></a>Chapter 2</h1>
    <h3><a name="d1e38">Introduction</a></h3>
    <h1><a name="d1e41"></a>Chapter 3</h1>
    <h3><a name="d1e43">Introduction</a></h3>
  </body>
</html>

I gather that generate-id is treating the path I build as a string, so 
it's not working.

My concern is not how to get generate-id to accept a string as a path 
(though I would be curious about that). My concern is to be able to create 
cross-references to identical elements (that do have different parent 
elements). I have total control over the XML input and the XSL stylesheet, 
so I am open to ANY idea that will get the job done.

Thanks very much.

Jay Bryant
Bryant Communication Services


<Prev in Thread] Current Thread [Next in Thread>