Dynamic/Generic XSLT for Verical and Horizontal Layouts - xslt

I have the following XSLT. It works great for horizontal layouts. However when there are too many columns I need it to flip to a vertical layout. Ultimately being able to configure or specify the number of columns in a variable would be best. Can anyone kindly help?
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/*">
<table>
<thead>
<tr>
<xsl:apply-templates select="*[1]/*" mode="th"/>
</tr>
</thead>
<tbody>
<xsl:apply-templates select="*"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="/*/*/*" mode="th">
<th>
<xsl:value-of select="name()"/>
</th>
</xsl:template>
<xsl:template match="/*/*">
<tr>
<xsl:apply-templates select="*"/>
</tr>
</xsl:template>
<xsl:template match="/*/*/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>

I am not sure I have understood the question correctly but if you just want to output several tables, each having $cols-per-table columns, then I think the adaption of the presented code to
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:param name="cols-per-table" select="10"/>
<xsl:output method="html" indent="yes" doctype-system="about:legacy-doctype"/>
<xsl:template match="/">
<xsl:apply-templates select="*/*[1]/*[position() mod $cols-per-table = 1]" mode="page"/>
</xsl:template>
<xsl:template match="*" mode="page">
<xsl:param name="page-no" select="position()"/>
<table border="1">
<thead>
<xsl:apply-templates select=". | following-sibling::*[position() < $cols-per-table]" mode="th"/>
</thead>
<tbody>
<xsl:apply-templates select="/*/*">
<xsl:with-param name="page-no" select="$page-no"/>
</xsl:apply-templates>
</tbody>
</table>
</xsl:template>
<xsl:template match="/*/*/*" mode="th">
<th>
<xsl:value-of select="name()"/>
</th>
</xsl:template>
<xsl:template match="/*/*">
<xsl:param name="page-no"/>
<xsl:variable name="first-col" select="1 + $cols-per-table * ($page-no - 1)"/>
<xsl:variable name="last-col" select="$first-col + $cols-per-table - 1"/>
<tr>
<xsl:apply-templates select="*[position() >= $first-col and position() <= $last-col]"/>
</tr>
</xsl:template>
<xsl:template match="/*/*/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyRYYjr/1
I have used XSLT 1.0 as the original code used that version although with XSLT 2/3 and positional grouping using for-each-group and/or a key computing the "pag" a "column" element belongs to might be more efficient:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="cols-per-table" as="xs:integer" select="10"/>
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:key name="page-no" match="/*/*/*">
<xsl:variable name="index" as="xs:integer">
<xsl:number/>
</xsl:variable>
<xsl:sequence select="($index - 1) idiv $cols-per-table + 1"/>
</xsl:key>
<xsl:template match="/">
<xsl:for-each-group select="*/*[1]/*" group-adjacent="(position() - 1) idiv $cols-per-table">
<xsl:apply-templates select="." mode="page">
<xsl:with-param name="page-no" select="position()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="*" mode="page">
<xsl:param name="page-no" tunnel="yes"/>
<table border="1">
<thead>
<xsl:apply-templates select="current-group()" mode="th"/>
</thead>
<tbody>
<xsl:apply-templates select="/*/*"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="/*/*/*" mode="th">
<th>
<xsl:value-of select="name()"/>
</th>
</xsl:template>
<xsl:template match="/*/*">
<xsl:param name="page-no" tunnel="yes"/>
<tr>
<xsl:apply-templates select="key('page-no', $page-no, .)"/>
</tr>
</xsl:template>
<xsl:template match="/*/*/*">
<td>
<xsl:value-of select="."/>
</td>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/jyRYYjr/4
At https://xsltfiddle.liberty-development.net/jyRYYjr/5 I have implemented a pure XSLT 3 approach solely using for-each-group and storing the "pages" and "columns" in a map.

Related

XSLT 2.0: Issues creating HTML table with dynamic rows & columns

I'm creating an HTML table that is based on dynamic columns(Hostname) and rows(VLAN). I'm running into an issue after the first position data(1 row for all hosts) in the is written to the table; selects the 2nd position data just fine, but the $vCol variable takes it back to the first line of the $vCols variable.
Appreciate any direction you can offer. Thanks
XSLT-2.0 code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:key name="kHostNameByValue" match="Hostname" use="."/>
<xsl:key name="kVLAN" match="Hostname" use="."/>
<xsl:variable name="vCols" select="/*/*/Hostname[generate-id()=generate-id(key('kHostNameByValue',.)[1])]"/>
<xsl:variable name="vMaxRows">
<xsl:for-each select="$vCols">
<xsl:sort data-type="number" order="descending" select="count(key('kVLAN', .))"/>
<xsl:if test="position() = 1">
<xsl:value-of select="count(key('kVLAN', .))"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="DocumentRoot">
<table border="1">
<!-- Print out column headings by Hostname -->
<tr>
<xsl:apply-templates select="$vCols"/>
</tr>
<!-- Print out VLANs by Hostname -->
<xsl:for-each-group select="(//Row)[not(position() > $vMaxRows)]" group-by="VLAN">
<tr>
<xsl:variable name="vPos" select="position()"/>
<!-- Issue on 2nd position when $vCols goes back to 1st hostname at line 3 -->
<xsl:for-each select="$vCols">
<td>
<xsl:value-of select="..[$vPos]/VLAN"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
<xsl:template match="Hostname">
<td>
<b>
<xsl:value-of select="." />
</b>
</td>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Here's the sample XML data.
<?xml version="1.0" encoding="UTF-8"?>
<DocumentRoot>
<Row>
<Hostname>switch-1</Hostname>
<HostIP>10.29.178.102</HostIP>
<VLAN>10</VLAN>
<VLANName>VLAN-10</VLANName>
</Row>
<Row>
<Hostname>switch-1</Hostname>
<HostIP>10.29.178.102</HostIP>
<VLAN>500</VLAN>
<VLANName>VLAN-500</VLANName>
</Row>
<Row>
<Hostname>switch-2</Hostname>
<HostIP>10.29.178.103</HostIP>
<VLAN>11</VLAN>
<VLANName>VLAN-11</VLANName>
</Row>
<Row>
<Hostname>switch-2</Hostname>
<HostIP>10.29.178.103</HostIP>
<VLAN>501</VLAN>
<VLANName>VLAN-500</VLANName>
</Row>
<Row>
<Hostname>switch-3</Hostname>
<HostIP>10.29.170.1</HostIP>
<VLAN>15</VLAN>
<VLANName>VLAN-15</VLANName>
</Row>
<Row>
<Hostname>switch-3</Hostname>
<HostIP>10.29.170.1</HostIP>
<VLAN>25</VLAN>
<VLANName>VLAN-25</VLANName>
</Row>
<Row>
<Hostname>switch-3</Hostname>
<HostIP>10.29.170.1</HostIP>
<VLAN>35</VLAN>
<VLANName>VLAN-35</VLANName>
</Row>
<Row>
<Hostname>switch-3</Hostname>
<HostIP>10.29.170.1</HostIP>
<VLAN>45</VLAN>
<VLANName>VLAN-45</VLANName>
</Row>
<Row>
<Hostname>switch-3</Hostname>
<HostIP>10.29.170.1</HostIP>
<VLAN>55</VLAN>
<VLANName>VLAN-55</VLANName>
</Row>
</DocumentRoot>
Output (Actual and desired):
Firstly, I think your maxRows variable can be simplified to this
<xsl:variable name="maxRows" select="max(//Row/count(key(hostNameByValue, Hostname)))" />
Where I have defined hostNameByValue key like so:
<xsl:key name="hostNameByValue" match="Row" use="Hostname"/>
You can also use distinct-values to get the distinct column names
<xsl:variable name="cols" select="distinct-values(//Row/Hostname)" />
So, assuming $rowNum is the current number (within a <xsl:for-each select="1 to $maxRows"> block, the code to get the current cell value would be this
<xsl:for-each select="$cols">
<th><xsl:value-of select="key('hostNameByValue', ., $doc)[position() = $rowNum]/VLAN"/></th>
</xsl:for-each>
(Where $doc is a reference to the initial XML document, because within the xsl:for-each is now a sequence of atomic values)
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="hostNameByValue" match="Row" use="Hostname"/>
<xsl:variable name="cols" select="distinct-values(//Row/Hostname)" />
<xsl:variable name="maxRows" select="max(//Row/count(key('hostNameByValue', Hostname)))" />
<xsl:variable name="doc" select="/" />
<xsl:template match="DocumentRoot">
<table>
<tr>
<xsl:for-each select="$cols">
<th><xsl:value-of select="."/></th>
</xsl:for-each>
</tr>
<xsl:for-each select="1 to $maxRows">
<xsl:variable name="rowNum" select="position()"/>
<tr>
<xsl:for-each select="$cols">
<th><xsl:value-of select="key('hostNameByValue', ., $doc)[position() = $rowNum]/VLAN"/></th>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/6r5Gh3N
I think using a group by might make this more complicated than it needs to be. Basically for each row you need to iterate over all columns and output the cell, if it exists, or an empty cell otherwise. This means you should iterate over an index rather then row elements:
<xsl:for-each select="1 to $numRows">
<xsl:variable name="rowIndex" select="position()" />
<tr>
<xsl:for-each select="$vCols">
<xsl:variable name="cell" select="//Row[string(Hostname) = .][position() = $rowIndex]" />
<xsl:apply-templates select="$cell">
<xsl:if test="not($cell)">
<td></td>
</xsl:if>
</xsl:for-each>
<tr>
</xsl:for.each>
I don't think in XSLT 2 or 3, once you use for-each-group, you need any of the keys, you can just store the grouping result and then process it, for instance to store the grouping result as XML in XSLT 2 or 3 you could use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="DocumentRoot">
<table>
<xsl:variable name="cols" as="element(col)*">
<xsl:for-each-group select="Row" group-by="Hostname">
<col name="{current-grouping-key()}">
<xsl:sequence select="current-group()"/>
</col>
</xsl:for-each-group>
</xsl:variable>
<thead>
<tr>
<xsl:for-each select="$cols">
<th>{#name}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:variable name="rows" select="max($cols!count(Row))"/>
<xsl:for-each select="1 to $rows">
<xsl:variable name="row" select="."/>
<tr>
<xsl:for-each select="$cols">
<td>{Row[$row]/VLAN}</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94rmq6Q/3 is XSLT 3 with the ! map operator and the text value templates but https://xsltfiddle.liberty-development.net/94rmq6Q/4 rewrites that as XSLT 2 with value-of instead.
Or in XSLT 3 you could store the grouping result in a sequence of arrays:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:output method="html" indent="yes" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="DocumentRoot">
<table>
<xsl:variable name="cols" as="array(element(Row))*">
<xsl:for-each-group select="Row" group-by="Hostname">
<xsl:sequence select="array{ current-group() }"/>
</xsl:for-each-group>
</xsl:variable>
<thead>
<tr>
<xsl:for-each select="$cols">
<th>{?1/Hostname}</th>
</xsl:for-each>
</tr>
</thead>
<tbody>
<xsl:variable name="rows" select="max($cols!array:size(.))"/>
<xsl:for-each select="1 to $rows">
<xsl:variable name="row" select="."/>
<tr>
<xsl:for-each select="$cols">
<td>{if ($row le array:size(.))
then .($row)/VLAN
else ()}</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94rmq6Q/2

XSLT - format comma seperated list into table format

A web service is returning a list of persons in the below xml format. The values are seperated by a semicolon. I need to display the values in 2 or 3 columns (Needs to be a variable).
Desired Results:
<table border="1">
<tr>
<td>Smith, John</td>
<td>Jackson, Samuel</td>
<td>Wayne, Bruce</td>
</tr>
<tr>
<td>Cosby, Bill</td>
<td>Kent, Clarke</td>
<td>Leno, Jay</td>
</tr>
<tr>
<td>OBrian, Conan</td>
<td> </td>
<td> </td>
</tr>
</table>
Xml sample
<?xml version="1.0" encoding="UTF-8"?> <PI>Smith, John; Jackson,
Samuel; Wayne, Bruce; Cosby, Bill; Kent, Clarke; Leno, Jay; OBrian,
Conan; </PI>
Please accept Holman's solution. His technique is spot-on.
XSLT 2.0 Solution
Your sample input data suggests semicolon terminated list, rather than a semicolon separated list.
This XSLT 2.0 stylesheet will work correctly with either a semicolon terminated list or a semicolon separated list. It takes one input parameter col-count, which defaults to 3.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="col-count" select="3" />
<xsl:template match="/*">
<table border="1">
<xsl:variable name="names" select="tokenize(.,';')[normalize-space(.)]" />
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ''"
group-by="(position() - 1) idiv $col-count">
<tr>
<xsl:for-each select="current-group()">
<td><xsl:value-of select="normalize-space(.)" /></td>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
Notes:
(1) Notice the brevity of the solution, and the absence of clumsy xsl:if statements. The use of calculated sequences (as opposed to input document node selections), is not just limited to the group-by attribute, but can also to applied to select. The use of computed sequences suggests a more functional view, rather than a procedural view.
(2) If you want the output to be safe for old browsers, then replace ...
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ''"
group-by="(position() - 1) idiv $col-count">
... with ...
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ' '"
group-by="(position() - 1) idiv $col-count">
XSLT 1.0 Solution
If you are stuck on XSLT 1.0, then you could use this equivalent, but less efficient stylesheet ...
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:esl="http://exslt.org/common"
xmlns:so="http://stackoverflow.com/questions/18066463"
version="1.0"
exclude-result-prefixes="xsl so esl">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="col-count" select="3" /><!-- Must be 2 or 3. -->
<xsl:variable name="empty-rtf">
<so:name> </so:name>
</xsl:variable>
<xsl:variable name="empty" select="esl:node-set($empty-rtf)/*" />
<xsl:template match="/*">
<table border="1">
<xsl:variable name="names-rtf">
<xsl:call-template name="split">
<xsl:with-param name="list" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="names" select="esl:node-set($names-rtf)/*" />
<xsl:for-each select="$names[(position() mod $col-count) = 1]">
<xsl:variable name="row" select="position() - 1" />
<tr>
<xsl:apply-templates select="$names[floor((position() - 1) div $col-count) = $row]" />
<xsl:if test="position()=last()">
<xsl:for-each select="($names|$empty)[position() <=
(($col-count - (count($names) mod $col-count)) mod $col-count)]">
<xsl:apply-templates select="$empty" />
</xsl:for-each>
</xsl:if>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="split">
<xsl:param name="list" />
<xsl:choose>
<xsl:when test="contains($list,';')">
<xsl:call-template name="split">
<xsl:with-param name="list" select="substring-before($list,';')" />
</xsl:call-template>
<xsl:call-template name="split">
<xsl:with-param name="list" select="substring-after($list,';')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="normalize-space($list)">
<so:name><xsl:value-of select="normalize-space($list)" /></so:name>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
<xsl:template match="so:*" priority="2">
<td><xsl:value-of select="." /></td>
</xsl:template>
</xsl:stylesheet>
Here is an XSLT 2.0 solution with the number of columns as a variable as requested. This employs a technique I teach in the classroom that underscores that grouping need not be by values in the XML (as many XSLT users assume), rather, it can be by any arbitrary calculation (in this case, the result of division).
[edited to show multiple invocations with different number of columns]
t:\ftemp>type names.xml
<?xml version="1.0" encoding="UTF-8"?> <PI>Smith, John; Jackson,
Samuel; Wayne, Bruce; Cosby, Bill; Kent, Clarke; Leno, Jay; OBrian,
Conan; </PI>
t:\ftemp>call xslt2 names.xml names.xsl names.out.xml "cols=3"
t:\ftemp>type names.out.xml
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
<tr>
<td>Smith, John</td>
<td>Jackson, Samuel</td>
<td>Wayne, Bruce</td>
</tr>
<tr>
<td>Cosby, Bill</td>
<td>Kent, Clarke</td>
<td>Leno, Jay</td>
</tr>
<tr>
<td>OBrian, Conan</td>
<td/>
<td/>
</tr>
</table>
t:\ftemp>call xslt2 names.xml names.xsl names.out.xml "cols=2"
t:\ftemp>type names.out.xml
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
<tr>
<td>Smith, John</td>
<td>Jackson, Samuel</td>
</tr>
<tr>
<td>Wayne, Bruce</td>
<td>Cosby, Bill</td>
</tr>
<tr>
<td>Kent, Clarke</td>
<td>Leno, Jay</td>
</tr>
<tr>
<td>OBrian, Conan</td>
<td/>
</tr>
</table>
t:\ftemp>type names.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:param name="cols" required="yes"/>
<xsl:template match="PI">
<table border="1">
<!--determine population and group by number of columns-->
<xsl:for-each-group select="tokenize(.,';\s+')"
group-by="(position()-1) idiv $cols">
<tr>
<!--put members into the row-->
<xsl:for-each select="current-group()">
<td>
<xsl:value-of select="normalize-space(.)"/>
</td>
</xsl:for-each>
<!--filler for the last row-->
<xsl:if test="position()=last()">
<xsl:for-each select="count(current-group())+1 to $cols">
<td/>
</xsl:for-each>
</xsl:if>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
t:\ftemp>rem

How can I group homogenous data in xsl?

Let's say we have following data:
<all>
<item id="1"/>
<item id="2"/>
...
<item id="N"/>
</all>
What is the most elegant, xslt-ish way to group those items?
For example, imagine we want a table with two cells in each row.
Off the top of my head I can imagine (not tested though)
in template, matching item, I can call this very item, selecting following-sibling.
But even in this case I should pass additional param, to make recursion finite.
As row-count can be variable .. am passing it as a param to the template .. :)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/all[node]">
<table>
<xsl:for-each select="node[1]">
<xsl:call-template name="whoaa">
<xsl:with-param name="count" select="'1'"/>
<xsl:with-param name="row_count" select="'10'"/>
<!--maximum row_count is set to 10 -->
</xsl:call-template>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="whoaa">
<xsl:param name="count"/>
<xsl:param name="row_count"/>
<!--check if we have crossed row_count-->
<xsl:if test="not ($row_count < $count)">
<tr>
<td>
<xsl:value-of select="."/>
</td>
<td>
<!--copy next column-->
<xsl:for-each select="following-sibling::node[1]">
<xsl:value-of select="."/>
</xsl:for-each>
</td>
</tr>
<!--Select next row .. call the same template untill we reach (row_count > count)-->
<xsl:for-each select="following-sibling::node[2]">
<xsl:call-template name="whoaa">
<xsl:with-param name="count" select="$count+2"/>
<xsl:with-param name="row_count" select="$row_count"/>
</xsl:call-template>
</xsl:for-each>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
use position and mod, e.g.
<xsl:template match="/all">
<table>
<xsl:apply-templates name="item" mode="group"/>
</table>
</xsl:template>
<xsl:template match="item[position() mod 2=1]" mode="group">
<tr>
<td><xsl:apply-templates select="." mode="render"/></td>
<td><xsl:apply-templates select="following-sibling::item[1]" mode="render"/></td>
</tr>
</xsl:template>
<xsl:template match="item[position() mod 2=0]"></xsl:template>
<xsl:template match="item" mode="render">item: <xsl:value-of select="#id"/></xsl:template>

sorting decimals with XSLT

I am trying to sort some XML data, i have followed W3s tutorials, but my code doesn't work, whats wrong?
<xsl:for-each select="garage/car[colour='red']">
<xsl:apply-templates>
<xsl:sort select="number(price)" order="descending" data-type="number" />
</xsl:apply-templates>
<tr>
<td><xsl:value-of select="make"/></td>
<td><xsl:value-of select="model"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
XML example~:
<garage>
<car>
<make>vw</make>
<model>golf</model>
<color>red</color>
<price>5.99</price>
</car>
<car>
<make>ford</make>
<model>focus</model>
<color>black</color>
<price>3.66</price>
</car>
<car>
<make>honda</make>
<model>civic</model>
<color>red</color>
<price>15.99</price>
</car>
</garage>
So you want to show all the red cars in descending price order?
It helps if you are consistent with your spelling of color/colour! I am British and feel your pain!
Also you are mixing up the for-each and apply-templates (both work)
apply-templates method:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" indent="yes" />
<xsl:template match="/">
<xsl:apply-templates select="garage/car[color='red']">
<xsl:sort select="number(price)" order="descending" data-type="number" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="car[color='red']">
<tr>
<td>
<xsl:value-of select="make" />
</td>
<td>
<xsl:value-of select="model" />
</td>
<td>
<xsl:value-of select="price" />
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
for-each method:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="1.0" indent="yes" />
<xsl:template match="/">
<xsl:for-each select="garage/car[color='red']">
<xsl:sort select="number(price)" order="descending" data-type="number" />
<tr>
<td>
<xsl:value-of select="make" />
</td>
<td>
<xsl:value-of select="model" />
</td>
<td>
<xsl:value-of select="price" />
</td>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

xsl : getting specific node values from a table

My XML Code
<DBE:Attribute name="Test1" type="Table">
<DBE:Table>
<DBE:TableHeader>
<DBE:TableColumn>t1</DBE:TableColumn>
<DBE:TableColumn>t2</DBE:TableColumn>
<DBE:TableColumn>t3</DBE:TableColumn>
<DBE:TableColumn>t4</DBE:TableColumn>
<DBE:TableColumn>t5</DBE:TableColumn>
<DBE:TableColumn>t6</DBE:TableColumn>
<DBE:TableColumn>t7</DBE:TableColumn>
<DBE:TableColumn>t8</DBE:TableColumn>
<DBE:TableColumn>t9</DBE:TableColumn>
<DBE:TableColumn>t10</DBE:TableColumn>
<DBE:TableColumn>t11</DBE:TableColumn>
<DBE:TableColumn>t12</DBE:TableColumn>
<DBE:TableColumn>t13</DBE:TableColumn>
</DBE:TableHeader>
<DBE:TableRow>
<DBE:TableData>0300 </DBE:TableData>
<DBE:TableData/>
<DBE:TableData>25</DBE:TableData>
<DBE:TableData>25</DBE:TableData>
<DBE:TableData>2009/09/03</DBE:TableData>
<DBE:TableData/>
<DBE:TableData>BAG</DBE:TableData>
<DBE:TableData>rrr</DBE:TableData>
<DBE:TableData>Yes</DBE:TableData>
<DBE:TableData>12</DBE:TableData>
<DBE:TableData>2009/03/09</DBE:TableData>
<DBE:TableData>GO</DBE:TableData>
<DBE:TableData/>
</DBE:TableRow>
</DBE:Table>
</DBE:Attribute>
I would like my output to be ->
t7 t5 t1 t13 --> Header
---------------------------------------------------------------
BAG 2009/09/03 0300 GO --> ROW1
.............................................................. --> ROW2
and so on
My XSL code --> (for only selected values to be displayed)
<xsl:for-each select="DBE:Attribute[#name='Test1']/DBE:Table/DBE:TableRow">
<tr bgcolor="white">
<xsl:for-each select="DBE:TableData">
<td>
<xsl:value-of select="node()|*">
</xsl:value-of>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
You could create a selector variable that holds the numeric position of columns you want. See the variable named "selector" below. Once you have that, a simple if in your TableData and TableColumn templates does the rest.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:DBE="foo.bar">
<xsl:variable name="selector">;1;5;7;12;</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="DBE:Attribute">
<html>
<body><xsl:apply-templates/></body>
</html>
</xsl:template>
<xsl:template match="DBE:Table">
<table border="1">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="DBE:TableHeader">
<tr>
<xsl:apply-templates select="DBE:TableColumn"/>
</tr>
</xsl:template>
<xsl:template match="DBE:TableColumn">
<xsl:variable name="pos">
<xsl:text>;</xsl:text><xsl:value-of select="string(position())"/><xsl:text>;</xsl:text>
</xsl:variable>
<xsl:if test="contains($selector, $pos)">
<th>
<xsl:value-of select="."/>
</th>
</xsl:if>
</xsl:template>
<xsl:template match="DBE:TableRow">
<tr>
<xsl:apply-templates select="DBE:TableData"/>
</tr>
</xsl:template>
<xsl:template match="DBE:TableData">
<xsl:variable name="pos">
<xsl:text>;</xsl:text><xsl:value-of select="string(position())"/><xsl:text>;</xsl:text>
</xsl:variable>
<xsl:if test="contains($selector, $pos)">
<td>
<xsl:value-of select="."/>
</td>
</xsl:if>
</xsl:template>
</xsl:stylesheet>