Normalizing XML adjacency list model into HTML tree list - xslt

I've seen examples of transforming "adjacency model" XML but none that will do it quite right for a ul/li bullet list. Could someone give me a hint? It would be great if the solution could support typical adjacency model requirements and deal with multiple level nesting/recursion.
If the XML is:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root>
<row Id="2" Name="data" />
<row Id="3" Name="people" />
<row Id="4" Name="person" ParentId="3" />
<row Id="6" Name="folder" ParentId="2" />
<row Id="7" Name="thing" ParentId="3" />
<row Id="8" Name="web" />
<row Id="9" Name="link" ParentId="8" />
</root>
And I use something like:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<ul id="someid" class="menu">
<xsl:apply-templates select="root/row[not(#ParentId)]"/>
</ul>
</xsl:template>
<xsl:template match="row">
<ul>
<li>
<xsl:variable name="ID" select="#Id"/>
<xsl:attribute name="rel">
<xsl:value-of select="#Id"/>
</xsl:attribute>
<xsl:value-of select="#Name"/>
<xsl:apply-templates select="//row[#ParentId=$ID]"/>
</li>
</ul>
</xsl:template>
</xsl:stylesheet>
Then I get:
<ul id="someid" class="menu">
<ul>
<li rel="2">
data<ul>
<li rel="6">folder</li>
</ul>
</li>
</ul>
<ul>
<li rel="3">
people
<ul>
<li rel="4">person</li>
</ul><ul>
<li rel="7">thing</li>
</ul>
</li>
</ul>
<ul>
<li rel="8">
web<ul>
<li rel="9">link</li>
</ul>
</li>
</ul>
</ul>
Note the extra close/open ul tags between the "person" and "thing" li's- shouldn't be there. I can see why it's happening but just not sure how to change the code to fix it.
Thanks.

Updated to reflect OP new requests
This is a recursive template which does an HTML-compliant nested list as deinfed in the W3C specs.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="/root">
<ul id="someid" class="menu">
<xsl:apply-templates select="row[not(#ParentId)]"/>
</ul>
</xsl:template>
<xsl:template match="row">
<li rel="{#Id}">
<xsl:value-of select="#Name"/>
<xsl:if test="count(../row[#ParentId=current()/#Id])>0">
<ul>
<xsl:apply-templates select="../row[#ParentId=current()/#Id]"/>
</ul>
</xsl:if>
</li>
</xsl:template>
</xsl:stylesheet>
Applied on this input:
<root>
<row Id="1" Name="data" />
<row Id="2" Name="data" />
<row Id="3" Name="people" />
<row Id="4" Name="person" ParentId="3" />
<row Id="6" Name="folder" ParentId="2" />
<row Id="7" Name="thing" ParentId="3" />
<row Id="8" Name="web" />
<row Id="9" Name="link" ParentId="8" />
<row Id="10" Name="anotherone" ParentId="9" />
<row Id="11" Name="anotherone" ParentId="9" />
<row Id="12" Name="anotherone" ParentId="9" />
<row Id="13" Name="anotherone" ParentId="3" />
</root>
Produces:
<ul id="someid" class="menu">
<li rel="1">data</li>
<li rel="2">data<ul>
<li rel="6">folder</li>
</ul>
</li>
<li rel="3">people<ul>
<li rel="4">person</li>
<li rel="7">thing</li>
<li rel="13">anotherone</li>
</ul>
</li>
<li rel="8">web<ul>
<li rel="9">link<ul>
<li rel="10">anotherone</li>
<li rel="11">anotherone</li>
<li rel="12">anotherone</li>
</ul>
</li>
</ul>
</li>
</ul>
If you are in trouble about how list should be created, try them at W3School.

You are really close, you just need to move your <xsl:variable name="ID" select="#Id"/> and your <xsl:apply-templates select="//row[#ParentId=$ID]"/> outside of your <ul>...</ul> statement.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<ul id="someid" class="menu">
<xsl:apply-templates select="root/row[not(#ParentId)]"/>
</ul>
</xsl:template>
<xsl:template match="row">
<xsl:variable name="ID" select="#Id"/>
<li>
<xsl:attribute name="rel">
<xsl:value-of select="#Id"/>
</xsl:attribute>
<xsl:value-of select="#Name"/>
</li>
<xsl:if test="//row[#ParentId=$ID]">
<ul>
<xsl:apply-templates select="//row[#ParentId=$ID]"/>
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
this produces the result I think you are looking for
<ul id="someid" class="menu">
<li rel="2">data</li>
<ul>
<li rel="6">folder</li>
</ul>
<li rel="3">people</li>
<ul>
<li rel="4">person</li>
<li rel="7">thing</li>
</ul>
<li rel="8">web</li>
<ul>
<li rel="9">link</li>
</ul>
</ul>
this also removes the extra <ul> before the top level <li rel="2">

Related

XSLT multi-level list depending on category name

I want to build a multi level nested list, depending on the category name. This is my xml:
<UserDefinedTable xmlns="DotNetNuke/UserDefinedTable">
<Data>
<UserDefinedRowId>28</UserDefinedRowId>
<Category>KatOne</Category>
<Title>Level 1</Title>
</Data>
<Data>
<UserDefinedRowId>29</UserDefinedRowId>
<Category>KatOneSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>30</UserDefinedRowId>
<Category>KatOneSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>31</UserDefinedRowId>
<Category>KatTwo</Category>
<Title>Level 1</Title>
</Data>
<Data>
<UserDefinedRowId>32</UserDefinedRowId>
<Category>KatTwoSub</Category>
<Title>Level 2</Title>
</Data>
<Data>
<UserDefinedRowId>33</UserDefinedRowId>
<Category>KatTwoSub</Category>
<Title>Level 2</Title>
</Data>
</UserDefinedTable>
And this is my attempt at the XSLT template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:udt="DotNetNuke/UserDefinedTable" exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:variable name="prefix_param">udt_<xsl:value-of select="//udt:Context/udt:ModuleId" />_param</xsl:variable>
<xsl:template match="udt:Data" mode="list">
<xsl:for-each select="udt:Data">
<li>
<span>KatUpper</span>
<ul class="level-two">
<xsl:for-each select="udt:Data">
<li>
KatSub
</li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each>
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data" />
<xsl:if test="$currentData">
<ul class="my-list">
<xsl:apply-templates select="$currentData" mode="list">
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:template>
<xsl:template name="EditLink">
<xsl:if test="udt:EditLink">
<a href="{udt:EditLink}">
<img border="0" alt="edit" src="{//udt:Context/udt:ApplicationPath}/images/edit.gif" />
</a>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I would like to make a foreach that check if the cateogry name mathces this or that then it stays on upper level, and inside each iteration i would check for category name to have a nested list inside:
Something like this:
<ul class="my-list">
<li>
<span>KatOne</span>
<ul clasS="levelTwo">
<li>
KatOneSub
</li>
<li>
KatOneSub
</li>
<li>
KatOneSub
</li>
</ul>
</li>
<li>
<span>KatTwo</span>
<ul clasS="levelTwo">
<li>
KatTwoSub
</li>
<li>
KatTwoSub
</li>
</ul>
</li>
</ul>
Your current problem is that within the template matching udt:Data, you do <xsl:for-each select="udt:Data">, but this will be looking for child elements of the current Data element that also called Data. You should really be looking for siblings here.
In XSLT 1.0, you could make use of a key to look up the "Level 2" items based on the first preceding "Level 1" items
<xsl:key name="level2"
match="udt:Data[udt:Title='Level 2']"
use="preceding-sibling::udt:Data[udt:Title='Level 1'][1]/udt:UserDefinedRowId" />
You would then start off by selecting the "Level 1" items
<xsl:variable name="currentData" select="udt:Data[udt:Title='Level 1']" />
<xsl:apply-templates select="$currentData" mode="list" />
Then, to get the "Level 2" items for the current "Level 1" item, you can do use the key
<xsl:for-each select="key('level2', udt:UserDefinedRowId)">
Try this XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:udt="DotNetNuke/UserDefinedTable"
exclude-result-prefixes="udt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:key name="level2"
match="udt:Data[udt:Title='Level 2']"
use="preceding-sibling::udt:Data[udt:Title='Level 1'][1]/udt:UserDefinedRowId" />
<xsl:template match="udt:Data" mode="list">
<li>
<span>
<xsl:value-of select="udt:Category" />
</span>
<xsl:if test="key('level2', udt:UserDefinedRowId)">
<ul class="levelTwo">
<xsl:for-each select="key('level2', udt:UserDefinedRowId)">
<li>
<span>
<xsl:value-of select="udt:Category" />
</span>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</li>
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:variable name="currentData" select="udt:Data[udt:Title='Level 1']" />
<xsl:if test="$currentData">
<ul class="my-list">
<xsl:apply-templates select="$currentData" mode="list" />
</ul>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note, you should change expressions like Title="Level 1" accordingly if you have a better way of identifying Level 1 and Level 2 elements.

Xsl 1.0, Double grouping

I have a xsl template that groups items by their category. Now i want to group them together inside those categories.
my xsl script:
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:variable name="prefix_param">udt_<xsl:value-of select="//udt:Context/udt:ModuleId" />_param</xsl:variable>
<xsl:key name="data-by-Category" match="udt:Data" use="udt:Category" />
<xsl:template match="udt:Data" mode="list">
<li>
<xsl:value-of select="udt:Subcategory" disable-output-escaping="yes" />
<xsl:value-of select="udt:Information" disable-output-escaping="yes" />
</li>
</xsl:template>
<xsl:template match="/udt:UserDefinedTable">
<xsl:for-each select="udt:Data[count(. | key('data-by-Category', udt:Category)[1]) = 1]">
<xsl:sort select="udt:Zap" />
<span>
<h1>
<xsl:value-of select="udt:Category" disable-output-escaping="yes" /> </h1>
</span>
<xsl:variable name="currentData" select="key('data-by-Category', udt:Category)" />
<xsl:if test="$currentData">
<ul>
<xsl:apply-templates select="$currentData" mode="list">
<xsl:sort select="udt:PodCategory" order="ascending" />
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="EditLink">
<xsl:if test="udt:EditLink">
<a href="{udt:EditLink}">
<img border="0" alt="edit" src="{//udt:Context/udt:ApplicationPath}/images/edit.gif" />
</a>
</xsl:if>
</xsl:template>
The sample output i get now:
<h1>Category 1</h1>
<ul>
<li>Subcategory 1 info 1</li>
<li>Subcategory 2 info 1</li>
<li>Subcategory 2 info 2</li>
<li>Subcategory 2 info 3</li>
</ul>
<h1>Category 2</h1>
<ul>
<li>Subcategory 1 info 1</li>
<li>Subcategory 1 info 2</li>
<li>Subcategory 2 info 1</li>
</ul>
And this is the sample output i want to achieve:
<h1>Category 1</h1>
<h2>Subcategory 1</h2>
<ul>
<li>info 1</li>
</ul>
<h2>Subcategory 2</h2>
<ul>
<li>info 1</li>
<li>info 2</li>
<li>info 3</li>
</ul>
<h1>Category 2</h1>
<h2>Subcategory 1</h2>
<ul>
<li>info 1</li>
<li>info 2</li>
</ul>
<h2>Subcategory 1</h2>
<ul>
<li>info 1</li>
</ul>
I want to gtoup the items inside each group in a secondary group and display its title aswell.
Define a second key
<xsl:key name="data-by-Category-and-Subcategory" match="udt:Data" use="concat(udt:Category, '|', udt:Subcategory)" />
and then use e.g. that to identify sub groups
<xsl:template match="/udt:UserDefinedTable">
<xsl:for-each select="udt:Data[count(. | key('data-by-Category', udt:Category)[1]) = 1]">
<xsl:sort select="udt:Zap" />
<span>
<h1>
<xsl:value-of select="udt:Category" disable-output-escaping="yes" /> </h1>
</span>
<xsl:variable name="currentData" select="key('data-by-Category', udt:Category)" />
<xsl:for-each select="$currentData[generate-id() = generate-id(key('data-by-Category-and-Subcategory', concat(udt:Category, '|', udt:Subcategory))[1])]">
<h2><xsl:value-of select="udt:Subcategory"/></h2>
<xsl::variable name="$sub-group" select="key('data-by-Category-and-Subcategory', concat(udt:Category, '|', udt:Subcategory)"/>
<ul>
<xsl:apply-templates select="$sub-group" mode="list">
<xsl:sort select="udt:PodCategory" order="ascending" />
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:for-each>
</xsl:template>

How can I with XSLT get count of all preceding elements that have specified attribute name?

I have some XML file that contains elements with id attributes. I need to have id values in proper order counting from the root element. So instead of this one:
<body>
<p id="1">
<span id="3"/>
</p>
<p/>
<div id="8">
<p id="2"/>
<ul>
<li/>
<li id="9">
<span id="12"/>
</li>
<li/>
<li id="13">
<span id="7"/>
</li>
</ul>
</div>
</body>
I would like to have this:
<body>
<p id="1">
<span id="2"/>
</p>
<p/>
<div id="3">
<p id="4"/>
<ul>
<li/>
<li id="5">
<span id="6"/>
</li>
<li/>
<li id="7">
<span id="8"/>
</li>
</ul>
</div>
</body>
Use the identity transformation template plus one for the id attributes:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#id">
<xsl:attribute name="id">
<xsl:number count="*[#id]" level="any"/>
</xsl:attribute>
</xsl:template>
http://xsltransform.net/3NSSEvD

Composite C1 menu for Twitter-Boostrap

I am trying to create a nav menu in Composite C1 for the Bootstrap framework, but I am unsure how to get submenus to show up in the menu. Here is what I have
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:in="http://www.composite.net/ns/transformation/input/1.0"
xmlns:lang="http://www.composite.net/ns/localization/1.0"
xmlns:f="http://www.composite.net/ns/function/1.0"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="xsl in lang f">
<xsl:param name="sitemap" select="/in:inputs/in:result[#name='SitemapXml']/Page" />
<xsl:template match="/">
<html>
<head>
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<xsl:for-each select="$sitemap[#isopen='true']">
<li>
<a href="{#URL}">
<xsl:if test="#iscurrent='true'" >
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<xsl:value-of select="#MenuTitle" />
</a>
</li>
<xsl:apply-templates select="Page" />
</xsl:for-each>
</ul>
</div>
</div>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="Page">
<xsl:if test="count(#MenuTitle)">
<li>
<a href="{#URL}">
<xsl:if test="#isopen='true'" >
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<xsl:value-of select="#MenuTitle" />
</a>
</li>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I am unsure what I need to add to get the sub-menus. Suggestions?
Try out something like this
<xsl:template match="/">
<html>
<head>
</head>
<body>
<div class="container">
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<xsl:for-each select="$sitemap[#isopen='true']">
<li>
<xsl:if test="#iscurrent='true'" >
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<a href="{#URL}">
<xsl:value-of select="#MenuTitle" />
</a>
</li>
<xsl:apply-templates select="Page" />
</xsl:for-each>
</ul>
</div>
</div>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="Page">
<xsl:variable name="depth" select="#Depth" />
<xsl:variable name="isDropDown" select="count(./*) > 0 and $depth < 3" />
<xsl:if test="count(#MenuTitle)">
<li>
<xsl:if test="#isopen='true'" >
<xsl:attribute name="class">active</xsl:attribute>
</xsl:if>
<xsl:if test="$isDropDown" >
<xsl:attribute name="class">dropdown</xsl:attribute>
</xsl:if>
<a href="{#URL}">
<xsl:if test="$isDropDown" >
<xsl:attribute name="class">dropdown-toggle</xsl:attribute>
</xsl:if>
<xsl:if test="$isDropDown" >
<xsl:attribute name="data-toggle">dropdown</xsl:attribute>
</xsl:if>
<xsl:value-of select="#MenuTitle" />
<xsl:if test="$isDropDown" >
<b class="caret"></b>
</xsl:if>
</a>
<xsl:if test="$isDropDown">
<ul class="dropdown-menu">
<xsl:apply-templates select="./*" />
</ul>
</xsl:if>
</li>
</xsl:if>

Building a multi-level menu for umbraco

I'm trying to build a multi-level dropdrown CSS menu for a website I'm doing on the umbraco content management system.
I need to build it to have the following structure:
<ul id="nav">
<li>Page #1</li>
<li>
Page #2
<ul>
<li>Subpage #1</li>
<li>Subpage #2</li>
</ul>
</li>
</ul>
So now I'm trying to figure out how to do the nesting using XSLT. This is what I have so far:
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:param name="currentPage"/>
<!-- update this variable on how deep your menu should be -->
<xsl:variable name="maxLevelForMenu" select="4"/>
<xsl:template match="/">
<ul id="nav">
<xsl:call-template name="drawNodes">
<xsl:with-param
name="parent"
select="$currentPage/ancestor-or-self::node [#level=1]"
/>
</xsl:call-template>
</ul>
</xsl:template>
<xsl:template name="drawNodes">
<xsl:param name="parent"/>
<xsl:if test="umbraco.library:IsProtected($parent/#id, $parent/#path) = 0 or (umbraco.library:IsProtected($parent/#id, $parent/#path) = 1 and umbraco.library:IsLoggedOn() = 1)">
<xsl:for-each select="$parent/node [string(./data [#alias='umbracoNaviHide']) != '1' and #level <= $maxLevelForMenu]">
<li>
<a href="{umbraco.library:NiceUrl(#id)}">
<xsl:value-of select="#nodeName"/>
</a>
<xsl:if test="count(./node [string(./data [#alias='umbracoNaviHide']) != '1' and #level <= $maxLevelForMenu]) > 0">
<xsl:call-template name="drawNodes">
<xsl:with-param name="parent" select="."/>
</xsl:call-template>
</xsl:if>
</li>
</xsl:for-each>
</xsl:if>
</xsl:template>
What I can't seem to figure out is how to check if the first level (here Page #1 and Page #2) has any children, and if they do add the extra <ul> to contain the <li> children.
Anyone out there to point me in the right direction?
First off, no need pass the a parent parameter around. The context will transport this information.
Here is the XSL stylesheet that should solve your problem:
<!-- update this variable on how deep your menu should be -->
<xsl:variable name="maxLevelForMenu" select="4"/>
<!--- match the document root --->
<xsl:template match="/root">
<div id="nav">
<xsl:call-template name="SubTree" />
</div>
</xsl:template>
<!-- this will be called by xsl:apply-templates -->
<xsl:template match="node">
<!-- the node is either protected, or the user is logged on (no need to check for IsProtected twice) -->
<xsl:if test="umbraco.library:IsProtected($parent/#id, $parent/#path) = 0 or umbraco.library:IsLoggedOn() = 1">
<li>
<xsl:value-of select="#nodeName"/>
<xsl:call-template name="SubTree" />
</li>
</xsl:if>
</xsl:template>
<xsl:template name="SubTree">
<!-- render sub-tree only if there are any child nodes --->
<xsl:if test="node">
<ul>
<xsl:apply-templates select="node[data[#alias='umbracoNaviHide'] != '1'][#level <= $maxLevelForMenu]">
<!-- ensure sorted output of the child nodes --->
<xsl:sort select="#sortOrder" data-type="number" />
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:template>
This is the XML I tested it on (I don't know much about Umbraco, but after looking at some samples I hope I got close to an Umbraco document):
<root id="-1">
<node id="1" level="1" sortOrder="1" nodeName="Page #1">
<data alias="umbracoNaviHide">0</data>
</node>
<node id="2" level="1" sortOrder="2" nodeName="Page #2">
<data alias="umbracoNaviHide">0</data>
<node id="3" level="2" sortOrder="2" nodeName="Subpage #2.2">
<data alias="umbracoNaviHide">0</data>
</node>
<node id="4" level="2" sortOrder="1" nodeName="Subpage #2.1">
<data alias="umbracoNaviHide">0</data>
<node id="5" level="3" sortOrder="3" nodeName="Subpage #2.1.1">
<data alias="umbracoNaviHide">0</data>
</node>
</node>
<node id="6" level="2" sortOrder="3" nodeName="Subpage #2.3">
<data alias="umbracoNaviHide">1</data>
</node>
</node>
<node id="7" level="1" sortOrder="3" nodeName="Page #3">
<data alias="umbracoNaviHide">1</data>
</node>
</root>
This is the output:
<div id="nav">
<ul>
<li>Page #1</li>
<li>Page #2
<ul>
<li>Subpage #2.1
<ul>
<li>Subpage #2.1.1</li>
</ul>
</li>
<li>Subpage #2.2</li>
</ul>
</li>
</ul>
</div>
There is nothing very special about this problem. The following solution tests that the node-list for <xsl:apply-templates/>
is not empty, before applying the templates:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="vLevel" select="0"/>
<xsl:template match="root">
<xsl:variable name="vnextLevelNodes"
select="node[#level = $vLevel+1]"/>
<xsl:if test="$vnextLevelNodes">
<ul>
<xsl:apply-templates select="$vnextLevelNodes"/>
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="node">
<!-- the node is either protected, or the user is logged on (no need to check for IsProtected twice) -->
<!-- <xsl:if test=
"umbraco.library:IsProtected($parent/#id, $parent/#path) = 0
or
umbraco.library:IsLoggedOn() = 1"> -->
<xsl:if test="1">
<li>
<!-- <a href="{umbraco.library:NiceUrl(#id)}"> -->
<a href="'umbraco.library:NiceUrl(#id)'">
<xsl:value-of select="#nodeName"/>
</a>
<xsl:variable name="vnextLevelNodes"
select="node[#level = current()/#level+1]"/>
<xsl:if test="$vnextLevelNodes">
<ul>
<xsl:apply-templates select="$vnextLevelNodes"/>
</ul>
</xsl:if>
</li>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I have used the following XML source document:
<root id="-1">
<node id="1" level="1" sortOrder="1" nodeName="Page #1">
<data alias="umbracoNaviHide">0</data>
</node>
<node id="2" level="1" sortOrder="2" nodeName="Page #2">
<data alias="umbracoNaviHide">0</data>
<node id="3" level="2" sortOrder="2" nodeName="Subpage #2.2">
<data alias="umbracoNaviHide">0</data>
</node>
<node id="4" level="2" sortOrder="1" nodeName="Subpage #2.1">
<data alias="umbracoNaviHide">0</data>
<node id="5" level="3" sortOrder="3" nodeName="Subpage #2.1.1">
<data alias="umbracoNaviHide">0</data>
</node>
</node>
<node id="6" level="2" sortOrder="3" nodeName="Subpage #2.3">
<data alias="umbracoNaviHide">1</data>
</node>
</node>
<node id="7" level="1" sortOrder="3" nodeName="Page #3">
<data alias="umbracoNaviHide">1</data>
</node>
</root>
Also, I have commented out any code referencing Umbraco extension functions, as I don't have access to them.
When the above transformation is applied on this source XML document, the correct, wanted result is produced:
<ul>
<li>
Page #1
</li>
<li>
Page #2
<ul>
<li>
Subpage #2.2
</li>
<li>
Subpage #2.1
<ul>
<li>
Subpage #2.1.1
</li>
</ul>
</li>
<li>
Subpage #2.3
</li>
</ul>
</li>
<li>
Page #3
</li>
</ul>
Hope this helped.
Cheers,
Dimitre Novatchev