how to traverse xml file based on variables with XSL
I recently asked a question on which engine to parse my xml and decided to stick to XSL, but I can only get the job half done. (ref: Which language to use to parse xml for navigation)
I've managed to get as far as traversing down <root> --> <menu> --> <nav>
but any children under nav and my logic gets totally screwed up.
Question is how do I repeat my logic in xsl to go deeper in the xml child nodes, when the @path is the next child node tier?
Am I even doing this right? I feel like I'm missing something here and should be using templates somehow?
For example here is my xsl style sheet.
<?xml version="1.0" encoding="UTF-8"?>
<!--
Document : sitemap.xsl
Created on : 2 February 2011, 14:53
Author : Jared
Description:
Purpose of transformation follows.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:param name="page" select="default"/>
<xsl:template match="root">
<ul class="level-0 top-level">
<xsl:for-each select="*/nav">
<xsl:choose>
<xsl:when test="$page = '/index.aspx'">
<!-- level 0 -->
<li>
<a><xsl:attribute name="href"><xsl:value-of select="@path" /></xsl:attribute><xsl:value-of select="@name" /></a>
</li>
</xsl:when>
<xsl:when test="@path=$page and $page != '/index.aspx'">
<!-- level 2 -->
<li class="children-open current-menu-page">
<a><xsl:attribute name="href"><xsl:value-of select="@path" /></xsl:attribute><xsl:value-of select="@name" /></a>
<ul class="level-2 current-menu">
<xsl:for-each select="/root/menu/nav[@path=$page]/child::*">
<li>
<a><xsl:attribute name="href"><xsl:value-of select="@path" /></xsl:attribute><xsl:value-of select="@name" /><br /></a>
</li>
</xsl:for-each>
</ul>
</li>
</xsl:when>
<xsl:otherwise>
<li>
<a><xsl:attribute name="href"><xsl:value-of select="@path" /></xsl:attribute><xsl:value-of select="@name" /></a>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</ul>
</xsl:template>
</xsl:stylesheet>
XML file looks something like this:
<root name="menutest">
<menu>
<nav name="home" path="index.php" />
<nav name="menulink1" path="link1.php">
<nav name="menulink1child1" path="menulink1childlink1.php">
<nav name="menulink1child1child1"
path="menulink1childlink1childlink1.php" />
</nav>
</nav>
<nav name="menulink2" path="link2.php">
<nav name="menulink1child2" path="menulink2childlink2.php">
<nav name="menulink2child2child2"
path="menulink2childlink2childlink2.php" />
</nav>
</nav>
<nav name="menulink3" path="link3.php">
<nav name="menulink3child3" path="menulink3childlink3.php">
<nav name="menulink3child3child3"
path="menulink3childlink3childlink3.php" />
</nav>
</nav>
<nav name="menulink4" path="link4.php">
<nav name="menulink4child4"
path="menulink4childlink4.php">
<nav name="menulink4child4child4"
path="menulink4childlink4childlink4.php" />
</nav>
</nav>
</menu>
</root>
This basically only shows first ti开发者_C百科er <nav>
children based on param "$page".
Example(s) of how the menu works and which nodes selected, basically aligned <nav>
nodes are children, indented nodes are child of parents and so on etc etc. xml example http://fraudo.orconhosting.net.nz/xml.png
TIA Jared
Updated code snippet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:param name="page" select="'index.aspx'"/>
<xsl:template name="makeUL">
<ul class="level-2 current-menu">
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="nav">
<li>
<xsl:if test=".//@path = $page and $page != '/index.aspx'">
<xsl:attribute name="class"><xsl:text>children-open current-menu-page</xsl:text></xsl:attribute>
</xsl:if>
<a href="{@path}">
<xsl:value-of select="@name" />
</a>
<xsl:if test=".//@path = $page and $page != '/index.aspx'">
<xsl:call-template name="makeUL"/>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
I also echo out "ul" before the xml parsing, but I may have that wrong :).
echo '<ul class="level-0 top-level">'."\n\r";
echo $xsl->transformToXML($dom);
echo '</ul>'."\n\r";
If you want to expand the branch of tree containing the identified node, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:param name="page" select="'menulink1childlink1.php'"/>
<xsl:template match="menu" name="makeUL">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="nav">
<li>
<a href="{@path}">
<xsl:value-of select="@name" />
</a>
<xsl:if test=".//@path = $page">
<xsl:call-template name="makeUL"/>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
Output:
<ul>
<li><a href="index.php">home</a></li>
<li><a href="link1.php">menulink1</a>
<ul>
<li><a href="menulink1childlink1.php">menulink1child1</a>
<ul>
<li><a href="menulink1childlink1childlink1.php"
>menulink1child1child1</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="link2.php">menulink2</a></li>
<li><a href="link3.php">menulink3</a></li>
<li><a href="link4.php">menulink4</a></li>
</ul>
Rendered:
- home
- menulink1
- menulink1child1
- menulink1child1child1
- menulink1child1
- menulink2
- menulink3
- menulink4
Note: This is the only case where .//
abbreviation can be handy.
The root cause of your trouble is that you are trying to use "pull" processing - traditional constructs like xsl:for-each and xsl:choose, instead of writing it "the XSLT way" using template rules and xsl:apply-templates. It's usually possible to write your code in "pull" style, but experts avoid it because it tends to lead you into this kind of trouble.
I say "usually", but one case where it isn't possible is when your input data is recursive (nav elements within nav elements, indefinitely nested). You can only process recursive data using recursive code, and in this situation, the apply-templates style of coding becomes essential. Sit down with a tutorial and read some examples until you have understood the concept, and then use it in your coding.
精彩评论