开发者

XSLT templates and recursion

Im new to XSLT and am having some problems trying to format an XML document which has recursive nodes.

My XML Code:

Hopefully my XML shows:

  • All <item> are nested with <items>
  • An item can have either just attributes, or sub nodes
  • The level to which <开发者_StackOverflow中文版item> nodes are nested can be infinently deep
<?xml version="1.0" encoding="utf-8" ?> 
- <items>
  <item groupID="1" name="Home" url="//" /> 
- <item groupID="2" name="Guides" url="/Guides/">
- <items>
- <item groupID="26" name="Online-Poker-Guide" url="/Guides/Online-Poker-Guide/">
- <items>
- <item>
  <id>107</id> 
- <title>
- <![CDATA[ Poker Betting - Online Poker Betting Structures
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-betting-structures
  ]]> 
  </url>
  </item>
- <item>
  <id>114</id> 
- <title>
- <![CDATA[ Beginners&#39; Poker - Poker Hand Ranking
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-hand-ranking
  ]]> 
  </url>
  </item>
- <item>
  <id>115</id> 
- <title>
- <![CDATA[ Poker Terms - 4th Street and 5th Street
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-poker-terms
  ]]> 
  </url>
  </item>
- <item>
  <id>116</id> 
- <title>
- <![CDATA[ Popular Poker - The Popularity of Texas Hold&#39;em
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-popularity-texas-holdem
  ]]> 
  </url>
  </item>
- <item>
  <id>364</id> 
- <title>
- <![CDATA[ The Impact of Traditional Poker on Online Poker (and vice versa)
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-tradional-vs-online
  ]]> 
  </url>
  </item>
- <item>
  <id>365</id> 
- <title>
- <![CDATA[ The Ultimate, Absolute Online Poker Scandal
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/online-poker-scandal
  ]]> 
  </url>
  </item>
  </items>
- <items>
- <item groupID="27" name="Beginners-Poker" url="/Guides/Online-Poker-Guide/Beginners-Poker/">
- <items>
+ <item>
  <id>101</id> 
- <title>
- <![CDATA[ Poker Betting - All-in On the Flop
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/poker-betting-all-in-on-the-flop
  ]]> 
  </url>
  </item>
+ <item>
  <id>102</id> 
- <title>
- <![CDATA[ Beginners&#39; Poker - Choosing an Online Poker Room
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/beginners-poker-choosing-a-room
  ]]> 
  </url>
  </item>
+ <item>
  <id>105</id> 
- <title>
- <![CDATA[ Beginners&#39; Poker - Choosing What Type of Poker to Play
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/beginners-poker-choosing-type-to-play
  ]]> 
  </url>
  </item>
+ <item>
  <id>106</id> 
- <title>
- <![CDATA[ Online Poker - Different Types of Online Poker
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/online-poker
  ]]> 
  </url>
  </item>
+ <item>
  <id>109</id> 
- <title>
- <![CDATA[ Online Poker - Opening an Account at an Online Poker Site
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/online-poker-opening-an-account
  ]]> 
  </url>
  </item>
+ <item>
  <id>111</id> 
- <title>
- <![CDATA[ Beginners&#39; Poker - Poker Glossary
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/beginners-poker-glossary
  ]]> 
  </url>
  </item>
+ <item>
  <id>117</id> 
- <title>
- <![CDATA[ Poker Betting - What is a Blind?
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/poker-betting-what-is-a-blind
  ]]> 
  </url>
  </item>
- <item>
  <id>118</id> 
- <title>
- <![CDATA[ Poker Betting - What is an Ante?
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/poker-betting-what-is-an-ante
  ]]> 
  </url>
  </item>
+ <item>
  <id>119</id> 
- <title>
- <![CDATA[ Beginners Poker - What is Bluffing?
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/online-poker-what-is-bluffing
  ]]> 
  </url>
  </item>
- <item>
  <id>120</id> 
- <title>
- <![CDATA[ Poker Games - What is Community Card Poker?
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/online-poker-what-is-community-card-poker
  ]]> 
  </url>
  </item>
- <item>
  <id>121</id> 
- <title>
- <![CDATA[ Online Poker - What is Online Poker?
  ]]> 
  </title>
- <url>
- <![CDATA[ /Guides/Online-Poker-Guide/Beginners-Poker/online-poker-what-is-online-poker
  ]]> 
  </url>
  </item>
  </items>
  </item>
  </items>
  </item>
  </items>
  </item>
  </items>

The XSL code:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>

    <xsl:template name="loop">
        <xsl:for-each select="items/item">
            <ul>
            <li><xsl:value-of select="@name" /></li>
            <xsl:if test="@name and child::node()">
                <ul>
                    <xsl:for-each select="items/item">
                        <li><xsl:value-of select="@name" />test</li>
                    </xsl:for-each>
                </ul>
                <xsl:call-template name="loop" />
            </xsl:if>
            <xsl:if test="child::node() and not(@name)">
                <xsl:for-each select="/items">
                    <li><xsl:value-of select="id" /></li>
                </xsl:for-each>
            </xsl:if>
            </ul>
        </xsl:for-each>
        <xsl:for-each select="item/items/item">
            <li>hi</li>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="/" name="test">
            <xsl:call-template name="loop" />
    </xsl:template>

</xsl:stylesheet>

Im trying to write the XSL so that every <items> node will render a <ul> and every <items> node will render an <li>.

The XSL needs to be recursive because i cant tell how deep the nested nodes will go.

Can anyone help?

Regards, Al


This is easy. The XSLT processor does all the recursion and the looping for you, all you need to do is to specify templates for nodes you want to handle.

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <!-- <items> with <item> children becomes <ul> -->
  <xsl:template match="items[item]">
    <ul>
      <xsl:apply-templates select="item" />
    </ul>
  </xsl:template>

  <!-- <items> without <item> children is not handled -->
  <xsl:template match="items[not(item)]" />

  <!-- <item> with @name becomes <li> -->
  <xsl:template match="item[@name]">
    <li>
      <xsl:value-of select ="@name" />
      <xsl:apply-templates select="items" />
    </li>
  </xsl:template>

  <!-- <item> without @name becomes <li>, too -->
  <xsl:template match="item[not(@name)]">
    <li>
      <xsl:value-of select ="id" />
      <xsl:apply-templates select="items" />
    </li>
  </xsl:template>
</xsl:stylesheet>

The <xsl:apply-templates> always is the recursive/iterative step in XSLT. It takes any nodes that fit its select expression and finds templates for them.

Your job is it to craft an appropriate select expression, supply a template for every node you want to handle and otherwise get out of the way. ;-) Resist the urge to cram everything into one big template or use <xsl:for-each> just because it feels convenient - it is not. Separate templates create more reusable and maintainable, less deeply nested code, and XSLT processors are optimized for template handling, so this might even be the more efficient approach.


You should be able to do this without writing a loop, if I understand your needs correctly.

It's generally better to use a more declarative style, in this instance writing a template matching the <items> tag and converting it to a <ul> and another matching <item> converting it to a <li>. An <xsl:apply-templates/> call inside both templates would supply the recursion.


The following stylesheet performs the specified formatting. Note the use of xsl:apply-templates to recurse down the XML tree. See 5.4 Applying Template Rules for more information.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
        <xsl:apply-templates select="items"/>
    </xsl:template>

    <xsl:template match="items">
        <ul>
            <xsl:apply-templates select="item" />
        </ul>
    </xsl:template>

    <xsl:template match="item">
        <li>
            <xsl:choose>
                <xsl:when test="@name">
                    <xsl:value-of select="@name"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="id"/>
                </xsl:otherwise>
            </xsl:choose>
            <xsl:apply-templates select="items" />
        </li>
    </xsl:template>
</xsl:stylesheet>


I think you can just write the XSL-T to match on <item>. The only way recursion would matter would be if you wanted to retain parent/child relationships. Matching on <item> will be sufficient if your requirement is to map each one to a bullet in an unordered list


Would something like this be more what you're after?

<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" indent="yes"/>

  <xsl:template match="items" mode="loop">
    <ul>
      <xsl:for-each select="item">
        <li>
          <xsl:value-of select ="@name" />
          <xsl:value-of select="id" />
          <xsl:for-each select="items">
            <xsl:apply-templates select="." mode="loop" />
          </xsl:for-each>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>

  <xsl:template match="/" name="test">
    <xsl:apply-templates select="/items" mode="loop" />
  </xsl:template>
</xsl:stylesheet>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜