The function id()

In XSL we sometimes want to lookup nodes by an attribute value of type ID. We consider our product catalog from the section called “A product catalog”. The following XSL may be used to create XHtmll documents from <catalog> instances:

<xsl:template match="/catalog">
  <html>
    <head><title>Product catalog</title></head>
    <body>
      <h1>List of Products</h1>
      <xsl:apply-templates select="product"/>
    </body>
  </html>
</xsl:template>

<xsl:template match="product">
  <h2 id="{@id}" ❶><xsl:value-of select="title"/></h2>
  <xsl:apply-templates select="para"/>
</xsl:template>

<xsl:template match="para">
  <p><xsl:apply-templates select="text()|*" ❷/></p>
</xsl:template>

<xsl:template match="link">
  <a href="#{@ref}" ❸><xsl:value-of select="."/></a>
</xsl:template>

The ID attribute <product id="foo"> is unique within the document instance. We may thus use it as an unique string value in the generated Xhtml, too.

Mixed content consisting of text and <link> nodes.

We define a file local Xhtml reference to a product.

The <para> element from the example document instance containing a <link ref="homeTrainer"> reference will be formatted as:

<p>If you hate rain look <a href="#homeTrainer">here</a>.</p>

Now suppose we want to add the product's title Home trainer here to give the reader an idea about the product without clicking the hypertext link:

<p>If you hate rain look <a href="#homeTrainer">here</a> (Home trainer).</p>

This title text node is part of the <product>node being referenced from the current <para>:

Figure 962. A graphical representation of our <catalog>. Create comment in forum

(Animated PDF Version)

A graphical representation of our <catalog>.

The dashed line shows the IDREF based reference from the <link> to the <product> node.


In XSL we may follow ID reference by means of the built in function id(...):

<xsl:template match="link">
  <a href="#{@ref}"><xsl:value-of select="."/></a>
  <xsl:text> (</xsl:text>
  <xsl:value-of select="id(@ref)/title" ❶/>
  <xsl:text>)</xsl:text>
</xsl:template>

Evaluating id(@ref) at returns the first <product> node. We simply take its <title> value and embed it into a pair of braces. This way the desired text portion (Home trainer) gets added after the hypertext link.

exercise No. 57

Extending the memo style sheet by mixed content and itemized lists Create comment in forum

Q:

In Supporting <table> and internal references in book.xsd. we constructed a schema allowing itemized lists and mixed content for <book> instances. This schema also allowed to define <emphasis>, <table> and <link> elements being part of a mixed content definition. Extend the current book2html.xsl to account for these extensions.

As we already saw in our memo example itemized lists in XHTML are represented by the element <ul> containing <li> elements. Since <p> elements are also allowed to appear as children our itemized lists can be easily mapped to Xhtml tags. A<link> node may be transformed into <a href="..."> Xhtml node.

The table model is a simplified version of the XHTML table model. Read the XSL documentation of the element <xsl:copy-of/> for processing tables.

Tip

The Oxygen plugin's default configuration does not validate XML input documents. Using the id(...) function requires two IDE configuration steps:

Enable Saxon EE (Enterprise version) in your XSL transformation scenario:
Enable XML input validation using Window --> Preferences:

Caveat: With input validation enabled you will no longer be able to process invalid or well formed XML document instances.

A:

The full source code of the solution is available at (Online HTML version) ... book2html.1.xsl. We discuss some important aspects. The following table provides mapping rules from book.xsd to Xhtml:

Table 4. Mapping elements from book.xsd to Xhtml
book.xsd Xhtml
<book>/<title> <h1>
<chapter>/<title> <h2>
<para> (mixed content) <p>
<link href="foo"> <a href="foo">
<emphasis> <em>
<itemizedlist> <ul>
<listitem> <li>
<table>, <caption>,<tr>, <td> along with all attributes Identity copy

Since our table model is a subset of the HTML table model we may simply copy corresponding nodes to the output:

<xsl:template match="table">
  <xsl:copy-of select="."/>
</xsl:template>

Next we need rules for itemized lists and paragraphs. Our model already implements lists in a way that closely resembles XHTML lists. Since the structure are compatible we only have to provide a mapping:

<xsl:template match="para">
  <p id="{generate-id(.)}"><xsl:apply-templates select="text()|*" /></p>
</xsl:template>

<xsl:template match="itemizedlist">
  <ul><xsl:apply-templates select="listitem"/></ul>
</xsl:template>

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

Since all chapters are reachable via hypertext links from the table of contents we must supply a unique id value for all of them. Chapters and paragraphs may be referenced by <link> elements and thus both need a unique identity value. For simplicity we create both of them via generate-id(). In a more sophisticated solution the strategy would be slightly different:

  • If a <chapter> node does have an id attribute defined then take its value.

  • If a <chapter> node does not have an id attribute defined then use generate-id().

  • <para> nodes only get values in XHTML if they do have an id attribute defined. This is consistent since these nodes are never referenced from the table of contents. Thus an identity is only required if the <para> node is referenced by a <link>. If that is a case the <para> surely does have a defined identity value.

We also have to provide a hypertext link to the table of contents:

<xsl:template match="chapter">
  <h2 id="{generate-id(.)}" ❶>
    <a href="#{generate-id(/book)}" ❷><xsl:value-of select="title"/></a>
  </h2>
  <xsl:apply-templates select="para|itemizedlist|table"/>
</xsl:template>

Implementing the <link> element is somewhat more complicated. We cannot use the @ref attribute values itself as <a href="..."> attribute values since the target's identity string is generated via generate-id(). But we may follow the reference via the XPath id() function and then use the target's identity value:

<xsl:template match="link">
  <a href="#{generate-id(id(@linkend))}">
    <xsl:value-of select="."/>
  </a>
</xsl:template>

The call to id(@linkend) returns either a <chapter> or a <para> node since attributes of type ID are only defined for these two elements. Using this node as input to generate-id() returns the desired identity value to be used in the generated Xhtml.