Welcome to WebmasterWorld Guest from

Forum Moderators: open

Message Too Old, No Replies

XSLT - Recursive template? How to

Outputing elements which might containt 'themselves'

8:50 pm on Nov 24, 2006 (gmt 0)

Full Member

10+ Year Member

joined:July 7, 2003
votes: 0

I'm working on an XML complex project (database-generated XMLs transformed via XSLT and some other weird things...) and I'm facing some troubles...
I've some experience with simple XSLT files, that is: those that just have a <template match="/"... and do everything inside that. But now, I need to go further and I'm lost :(
So, that's the case: my XML files contain some 'item' elements, which might be the child of an 'inventory' element or another 'item' element. I need to transform them to a nested html unordered list (<ul> & <li> ). For example, given the following 'inventory':
<item name="Standard Sword" equipped="true" type="weapon" ammount="1"/>
<item name="Backpack" equipped="true" type="container" ammount="1">
<!-- now the fun part starts -->
<item name="Healing Potion (60)" type="potion" ammount="7"/>
<item name="Clothe bag" type="container" ammount="1">
<item name="Azurite (200gp)" type="gem" ammount="2"/>
<item name="Gold piece" type="money" ammount="48"/>
</item><!-- Clothe bag-->
</item><!-- Backpack-->

I'd like to generate the following HTML:
<ul class="inventory">
<li class="equipped"><img src="weapon.gif"/>Standard Sword</strong></li>
<li class="equipped"><img src="container.gif"/>Backpack</strong><ul>
<li><img src="potion.gif"/>7 &times; Healing Potion (60)</li>
<li><img src="container.gif"/>Clothe bag<ul>
<li><img src="gem.gif"/>2 &times; Azurite (200gp)</li>
<li><img src="money.gif"/>48 &times; Gold piece</li>
</ul><!-- this closes the Clothe bag list-->
</ul><!-- this closes the Backpack list-->
</ul><!-- this closes the initial list-->

So, my main question is: how do I perform this transformation with XSLT?
Also, I've a pair of related doubts, which come from the 'equipped' items: as can be seen in the code, I want to highlight them, using CSS. However, the equipped attribute is optional: how can I get it checked without crashing when it's missing? By the way, all the comments in the code are for readability, I don't pretend them to be generated ;P

Thanks for your attention, and greetings.
Herenvardö, Happy Hippie Heviatta (a.k.a. H4)

[edited by: Herenvardo at 8:51 pm (utc) on Nov. 24, 2006]

10:58 pm on Nov 27, 2006 (gmt 0)

Senior Member

WebmasterWorld Senior Member 10+ Year Member

joined:Dec 17, 2001
votes: 0

Recursion is the soul of XSLT.

At the end of your template, add an <xsl:apply-templates /> directive to instruct the processor to repeat the match, but on the current node set (i.e. first it passes through inventory/item s, then it will pass through the inventory/item/item s. ).

Your code has extraneous </strong> tags which I'll conveniently ignore. Here's a quick-and-dirty set of templates that will generate the output you're after:

<!-- create an unordered list with the class attribute value of "inventory" for each "inventory" element -->
<xsl:template match="inventory">
<ul class="inventory">

<!-- process the "item" child elements of inventory -->
<xsl:apply-templates select="item" />


<xsl:template match="item">
<!-- create a list item for every "item" element -->

<!-- does the "item" have an "equipped" attribute whose value is true? if so, create a class attribute on the li with the value "equipped" -->

<xsl:if test="@equipped='true'">
<xsl:attribute name="class">equipped</xsl:attribute>

<!-- does the "item" have a "type" attribute whose value is not null? if so, insert the value of "type" in the src attribute of the img -->
<xsl:if test="@type!= ''">
<img src="{@type}.gif" />

<!-- does the "item" have an "ammount" attribute whose value is greater than one? if so, insert the value of "ammount" and some text -->

<xsl:if test="@ammount &gt; 1">
<xsl:value-of select="@ammount" />
<xsl:text> &times; </xsl:text>

<!-- insert the "name" of the "item" -->

<xsl:value-of select="@name" />

<xsl:if test="item">
<!-- if the "item" has child items, create a new list and start over again -->

<xsl:apply-templates />