looping templates

R

Ruthless

Hello.

I've got a simple XML:

<?xml version="1.0" encoding="iso-8859-2"?>
<?xml-stylesheet type="text/xsl" href="4.xsl"?>

<struct>

<node level="1" no="1">

<node level="2" no="2" />
<node level="2" no="3" />
<node level="2" no="4" />
</node>

<node level="1" no="5">

<node level="2" no="6" />
<node level="2" no="7" />
<node level="2" no="8" />
</node>

</struct>

and i want to display all the levels starting from 1 to 2

I've got my xslt sth like this:

<?xml version="1.0" encoding="ISO-8859-2" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="html"/>

<xsl:template match="/">

<html>
<body>

<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
</tr>

<xsl:for-each select="//node">
<xsl:sort select="@level"/>

<xsl:if test="@level='1'">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:if>
</xsl:for-each>

</table>

<!-- now goes level 2-->

<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
</tr>

<xsl:for-each select="//node">
<xsl:sort select="@level"/>

<xsl:if test="@level='2'">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:if>
</xsl:for-each>

</table>
</body>
</html>

</xsl:template>

</xsl:stylesheet>

It works fine but it's useless when there would be more then 2 levels - how
can i loop this template to go through all the levels - each level has a new
table(just like above)

and second thing how can i count node with e.g. attributes level="2"?

I've tried sth like this:
<xsl:template match="/">
<xsl:variable name="ile" select="count(//node@level='2')" />
</xsl:template>

and then:

<b><xsl:value-of select="$ile"/></b>

but it's wrong and i've got warning msg.

thanks in advance
greetings R
 
D

Dimitre Novatchev

The way to do this is the following:

This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>

<xsl:template match="*[node]">
<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
<xsl:apply-templates select="node" mode="display"/>
</tr>
</table>
<br />
<br />
<xsl:apply-templates select="node"/>
</xsl:template>

<xsl:template match="node" mode="display">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>

When applied on this source.xml:

<struct>
<node level="1" no="1">
<node level="2" no="2" />
<node level="2" no="3">
<node level="3" no="4"/>
</node>
<node level="2" no="5" />
</node>
<node level="1" no="6">
<node level="2" no="7">
<node level="3" no="8"/>
<node level="3" no="9"/>
</node>
<node level="2" no="10" />
<node level="2" no="11" />
</node>
</struct>

Produces this output (as displayed in the browser):

Name No. Level

node 1 1

node 6 1




Name No. Level

node 2 2

node 3 2

node 5 2




Name No. Level

node 4 3




Name No. Level

node 7 2

node 10 2

node 11 2




Name No. Level

node 8 3

node 9 3


However you want all nodes belonging to the same level to be displayed in a
single table.

In this case this transformation:


<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:key name="kByLevel" match="node" use="@level"/>

<xsl:variable name="vMinMaxLevel">
<xsl:for-each
select="//node[generate-id()
=
generate-id(key('kByLevel', @level)[1])
]">
<xsl:sort select="@level" data-type="number"/>

<xsl:if test="position() = 1 or position() = last()">
<xsl:value-of select="@level"/>
<xsl:if test="position() = 1">|</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:variable>

<xsl:template match="/">
<xsl:call-template name="displayH">
<xsl:with-param name="pLevel"
select="substring-before($vMinMaxLevel, '|')"/>
<xsl:with-param name="pMaxLevel"
select="substring-after($vMinMaxLevel, '|')"/>

</xsl:call-template>
</xsl:template>

<xsl:template name="displayH">
<xsl:param name="pLevel" select="1"/>
<xsl:param name="pMaxLevel" select="1"/>

<xsl:variable name="vThisLevel"
select="key('kByLevel', $pLevel)"/>

<xsl:if test="$pLevel &lt;= $pMaxLevel
and $vThisLevel">
<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
<xsl:apply-templates select="$vThisLevel"/>
</tr>
</table>
<br />
<br />
<xsl:call-template name="displayH">
<xsl:with-param name="pLevel" select="$pLevel + 1"/>
<xsl:with-param name="pMaxLevel" select="$pMaxLevel"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

<xsl:template match="node">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>


when applied on the same source.xml produces the wanted output:

Name No. Level

node 1 1

node 6 1


Name No. Level

node 2 2

node 3 2

node 5 2

node 7 2

node 10 2

node 11 2


Name No. Level

node 4 3

node 8 3

node 9 3


This transformation works even in cases when levels are not consecutive
numbers (or even positive numbers) and when we do not know the starting
level.

Hope this helped.

Dimitre Novatchev.
FXSL developer, XML Insider,

http://fxsl.sourceforge.net/ -- the home of FXSL
Resume: http://fxsl.sf.net/DNovatchev/Resume/Res.html
 
R

Ruthless

Wow man

It will take me all night to understand your example ;D

but thanks, you helped me a lot

greetings R

U¿ytkownik "Dimitre Novatchev said:
The way to do this is the following:

This transformation:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>

<xsl:template match="*[node]">
<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
<xsl:apply-templates select="node" mode="display"/>
</tr>
</table>
<br />
<br />
<xsl:apply-templates select="node"/>
</xsl:template>

<xsl:template match="node" mode="display">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>

When applied on this source.xml:

<struct>
<node level="1" no="1">
<node level="2" no="2" />
<node level="2" no="3">
<node level="3" no="4"/>
</node>
<node level="2" no="5" />
</node>
<node level="1" no="6">
<node level="2" no="7">
<node level="3" no="8"/>
<node level="3" no="9"/>
</node>
<node level="2" no="10" />
<node level="2" no="11" />
</node>
</struct>

Produces this output (as displayed in the browser):

Name No. Level

node 1 1

node 6 1




Name No. Level

node 2 2

node 3 2

node 5 2




Name No. Level

node 4 3




Name No. Level

node 7 2

node 10 2

node 11 2




Name No. Level

node 8 3

node 9 3


However you want all nodes belonging to the same level to be displayed in a
single table.

In this case this transformation:


<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:key name="kByLevel" match="node" use="@level"/>

<xsl:variable name="vMinMaxLevel">
<xsl:for-each
select="//node[generate-id()
=
generate-id(key('kByLevel', @level)[1])
]">
<xsl:sort select="@level" data-type="number"/>

<xsl:if test="position() = 1 or position() = last()">
<xsl:value-of select="@level"/>
<xsl:if test="position() = 1">|</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:variable>

<xsl:template match="/">
<xsl:call-template name="displayH">
<xsl:with-param name="pLevel"
select="substring-before($vMinMaxLevel, '|')"/>
<xsl:with-param name="pMaxLevel"
select="substring-after($vMinMaxLevel, '|')"/>

</xsl:call-template>
</xsl:template>

<xsl:template name="displayH">
<xsl:param name="pLevel" select="1"/>
<xsl:param name="pMaxLevel" select="1"/>

<xsl:variable name="vThisLevel"
select="key('kByLevel', $pLevel)"/>

<xsl:if test="$pLevel &lt;= $pMaxLevel
and $vThisLevel">
<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
<xsl:apply-templates select="$vThisLevel"/>
</tr>
</table>
<br />
<br />
<xsl:call-template name="displayH">
<xsl:with-param name="pLevel" select="$pLevel + 1"/>
<xsl:with-param name="pMaxLevel" select="$pMaxLevel"/>
</xsl:call-template>
</xsl:if>
</xsl:template>

<xsl:template match="node">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:template>
</xsl:stylesheet>


when applied on the same source.xml produces the wanted output:

Name No. Level

node 1 1

node 6 1


Name No. Level

node 2 2

node 3 2

node 5 2

node 7 2

node 10 2

node 11 2


Name No. Level

node 4 3

node 8 3

node 9 3


This transformation works even in cases when levels are not consecutive
numbers (or even positive numbers) and when we do not know the starting
level.

Hope this helped.

Dimitre Novatchev.
FXSL developer, XML Insider,

http://fxsl.sourceforge.net/ -- the home of FXSL
Resume: http://fxsl.sf.net/DNovatchev/Resume/Res.html


Ruthless said:
Hello.

I've got a simple XML:

<?xml version="1.0" encoding="iso-8859-2"?>
<?xml-stylesheet type="text/xsl" href="4.xsl"?>

<struct>

<node level="1" no="1">

<node level="2" no="2" />
<node level="2" no="3" />
<node level="2" no="4" />
</node>

<node level="1" no="5">

<node level="2" no="6" />
<node level="2" no="7" />
<node level="2" no="8" />
</node>

</struct>

and i want to display all the levels starting from 1 to 2

I've got my xslt sth like this:

<?xml version="1.0" encoding="ISO-8859-2" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:eek:utput method="html"/>

<xsl:template match="/">

<html>
<body>

<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
</tr>

<xsl:for-each select="//node">
<xsl:sort select="@level"/>

<xsl:if test="@level='1'">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:if>
</xsl:for-each>

</table>

<!-- now goes level 2-->

<table align="center" border="1">
<tr>
<th>Name</th><th>No.</th><th>Level</th>
</tr>

<xsl:for-each select="//node">
<xsl:sort select="@level"/>

<xsl:if test="@level='2'">
<tr>
<td align="center"><xsl:value-of select="name()"/></td>
<td><xsl:value-of select="@no"/></td>
<td><xsl:value-of select="@level"/></td>
</tr>
</xsl:if>
</xsl:for-each>

</table>
</body>
</html>

</xsl:template>

</xsl:stylesheet>

It works fine but it's useless when there would be more then 2 levels - how
can i loop this template to go through all the levels - each level has a new
table(just like above)

and second thing how can i count node with e.g. attributes level="2"?

I've tried sth like this:
<xsl:template match="/">
<xsl:variable name="ile" select="count(//node@level='2')" />
</xsl:template>

and then:

<b><xsl:value-of select="$ile"/></b>

but it's wrong and i've got warning msg.

thanks in advance
greetings R
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,810
Latest member
Kassie0918

Latest Threads

Top