Jason
Jason

Reputation: 399

How do you create anchor links with XML and XSL?

Hey guys, what I am wanting to create is a mini timetable for a zoo.

Below is a ms paint mockup of the functionality. What im looking to have is a list of times horizontally up top, and when you click on one, the page moves down to that time slot and shows its details.

Exactly like anchor tags in HTML and my data is stored in XML.

enter image description here

So here is my xml data:

<zoo>
<animal name="Lion">
<feeding-time>11:00</feeding-time>
</animal>
<animal name="Penguin">
<feeding-time>14:00</feeding-time>
</animal>
<animal name="Elephant">
<feeding-time>9:00</feeding-time>
</animal>
<animal name="Tortoise">
<feeding-time>11:00</feeding-time>
</animal>
<animal name="Ape">
<feeding-time>16:00</feeding-time>
</animal>
<animal name="Hippo">
<feeding-time>14:00</feeding-time>
</animal>
<animal name="Rattle Snake">
<feeding-time>9:00</feeding-time>
</animal>
<animal name="Flamingo">
<feeding-time>15:00</feeding-time>
</animal>
</zoo>

And my XSL page is pretty bland:

<xsl:template match="/">
<html>
<head>
  <title>Real Estate Listings</title>
  <link href="style.css" rel="stylesheet" type="text/css" />
</head>
   <body>

   </body>
</html>
</xsl:template>

</xsl:stylesheet>

I know that I need to use Locator paths using axes and/or Meunchian grouping - and I have researched this for hours and I still dont have any idea whats going on.

I know that I need to use the generate-id function, and use the key function is well but again, I have no idea how to implement it - iv spent hours and hours on google trying to figure this stuff out.

Any help would be orsome.

Upvotes: 1

Views: 3899

Answers (2)

Flynn1179
Flynn1179

Reputation: 12075

Muenchian grouping uses a key defined at the root level of the XSLT sheet, that allows the key function to return a list of all elements that meet a given criteria, and then picks the first item from that list. For example:

<xsl:key name="feedingTime" match="*" use="feeding-time" />

This allows you to call key('feedingTime','11:00') to get a list of all elements who have a feeding-time element with a value of 11:00.

You can use this in a template with the generate-id() function, which returns a unique value of each ID. You do this by comparing the id of the element you're currently processing, with the id of the first element in the list of all elements with the same feeding-time value. Like this:

<xsl:if test="generate-id(feeding-time) = generate-id(key('feeding-time',feeding-time)[1])">
  <!-- generate output -->
</xsl:if>

Or, you can use the same condition in a template match, and use the same key to iterate through the list of animal nodes with the current feeding time:

<xsl:template match="animal[generate-id() = generate-id(key('feedingTime',feeding-time)[1])]">
  <!-- output a heading here, current node is first animal node with each feeding-time -->
  <xsl:for-each select="key('feedingTime',feeding-time)">
    <!-- output each animal here -->
  </xsl:for-each>
</xsl:template>

If you do this, you need to include an empty template like this: <xsl:template match="animal" /> to handle the remaining animal elements, discarding them; you already dealt with them in for-each loop of the above template.

You can use template modes to process the list separately for the links and the content below like this:

<xsl:key name="feedingTime" match="*" use="feeding-time" />

<xsl:template match="zoo">
  <xsl:apply-templates mode="links" />
  <xsl:apply-templates mode="content" />
</xsl:template>

<xsl:template match="animal[generate-id() = generate-id(key('feedingTime',feeding-time)[1])]" mode="links">
  <a href="#time_{feeding-time}">
    <xsl:value-of select="feeding-time" />
  </a>
</xsl:template>

<xsl:template match="animal[generate-id() = generate-id(key('feedingTime',feeding-time)[1])]" mode="content">
  <p id="time_{feeding-time}">
    <xsl:value-of select="concat(feeding-time,' Feeding time for:')" />
  </p>
  <ul>
    <xsl:for-each select="key('feedingTime',feeding-time)">
      <li>
        <xsl:value-of select="@name" />
      </li>
    </xsl:for-each>
  </ul>
</xsl:template>

<xsl:template match="animal" mode="links" />
<xsl:template match="animal" mode="content" />

The two templates at the bottom are to handle all animal elements that aren't handled by the previous ones (i.e., the 2nd and subsequent animal element for each feeding-time). I haven't handled the formatting here, but hopefully it should demonstrate techniques that should help you.

Upvotes: 0

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243599

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 
 <xsl:key name="kTimeByVal" match="feeding-time"
  use="."/>

 <xsl:key name="kAnimalByTime" match="@name"
  use="../feeding-time"/>
  
 <xsl:template match="/">
  <xsl:apply-templates/>
  
  <xsl:apply-templates mode="group"/>
 </xsl:template>

 <xsl:template match=
  "feeding-time[generate-id()
               =
                generate-id(key('kTimeByVal',.)[1])
               ]
  ">
  
  <a href="#{generate-id()}">
  <xsl:value-of select="."/>
  </a>
  <xsl:text> </xsl:text>
 </xsl:template>
 
 <xsl:template mode="group" match=
  "feeding-time[generate-id()
               =
                generate-id(key('kTimeByVal',.)[1])
               ]
  ">
  
  <br /><p id="{generate-id()}"><xsl:text/>

  <b><xsl:value-of select="."/> Feeding Time for:</b></p>
  
  <xsl:apply-templates select="key('kAnimalByTime', .)"/>
 </xsl:template>
 
 <xsl:template match="@name">
  <br /><xsl:value-of select="."/>
 </xsl:template>
 
 <xsl:template match="text()"/>
 <xsl:template mode="group" match="text()"/>
</xsl:stylesheet>

when applied to the provided XML document:

<zoo>
    <animal name="Lion">
        <feeding-time>11:00</feeding-time>
    </animal>
    <animal name="Penguin">
        <feeding-time>14:00</feeding-time>
    </animal>
    <animal name="Elephant">
        <feeding-time>9:00</feeding-time>
    </animal>
    <animal name="Tortoise">
        <feeding-time>11:00</feeding-time>
    </animal>
    <animal name="Ape">
        <feeding-time>16:00</feeding-time>
    </animal>
    <animal name="Hippo">
        <feeding-time>14:00</feeding-time>
    </animal>
    <animal name="Rattle Snake">
        <feeding-time>9:00</feeding-time>
    </animal>
    <animal name="Flamingo">
        <feeding-time>15:00</feeding-time>
    </animal>
</zoo>

produces exactly the wanted result:

<a href="#d0e5">11:00</a> 
<a href="#d0e11">14:00</a> 
<a href="#d0e17">9:00</a> 
<a href="#d0e29">16:00</a> 
<a href="#d0e47">15:00</a> 
<br/>
<p id="d0e5">
   <b>11:00 Feeding Time for:</b>
</p>
<br/>Lion<br/>Tortoise<br/>
<p id="d0e11">
   <b>14:00 Feeding Time for:</b>
</p>
<br/>Penguin<br/>Hippo<br/>
<p id="d0e17">
   <b>9:00 Feeding Time for:</b>
</p>
<br/>Elephant<br/>Rattle Snake<br/>
<p id="d0e29">
   <b>16:00 Feeding Time for:</b>
</p>
<br/>Ape<br/>
<p id="d0e47">
   <b>15:00 Feeding Time for:</b>
</p>
<br/>Flamingo

And it displays in the browser exactly as wanted, and has the wanted (link-clicking) behavior:

11:00 14:00 9:00 16:00 15:00

11:00 Feeding Time for:


Lion
Tortoise

14:00 Feeding Time for:


Penguin
Hippo

9:00 Feeding Time for:


Elephant
Rattle Snake

16:00 Feeding Time for:


Ape

15:00 Feeding Time for:


Flamingo

Explanation: Muenchian grouping, using generate-id() for generating unique ids to use as anchors, using keys.

Upvotes: 2

Related Questions