 |
| XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead. |
Welcome to the p2p.wrox.com Forums.
You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
|
|
|
|

November 9th, 2004, 12:13 PM
|
|
Registered User
|
|
Join Date: Nov 2004
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Creating a medal table
I would like to display the following XML as a table showing who got what medal i.e. 1st 2nd and 3rd (Gold, Silver Bronze)
The output I wishing to create would look like this:
Name Gold Silver Bronze
Player1 3 1 0
Player2 1 3 0
Player3 0 0 4
<games>
<records type="Type1">
<event name="event1" score="time">
<player name="Player1">
<record-score>10.00</record-score>
</player>
<player name="Player2">
<record-score>11.00</record-score>
</player>
<player name="Player3">
<record-score>12.00</record-score>
</player>
</event>
<event name="event2" score="time">
<player name="Player1">
<record-score>10.00</record-score>
</player>
<player name="Player2">
<record-score>15.00</record-score>
</player>
<player name="Player3">
<record-score>20.00</record-score>
</player>
</event>
</records>
<records type="Type2">
<event name="event3" score="points">
<player name="Player1">
<record-score>200</record-score>
</player>
<player name="Player2">
<record-score>100</record-score>
</player>
<player name="Player3">
<record-score>50</record-score>
</player>
</event>
<event name="event4" score="points">
<player name="Player1">
<record-score>10</record-score>
</player>
<player name="Player2">
<record-score>20</record-score>
</player>
<player name="Player3">
<record-score>5</record-score>
</player>
</event>
</records>
</games>
The attribute score on the event tag is used to determine the order i.e. "time" means the quickest time (lowest record-score) wins gold and "points" means the highest points (highest record-score) wins gold.
Example: Player1 wins gold on event1 as his time of 10.00 is quicker than Player2 11.00 and Player3 12.00
My XML has more events but I've inclued a small subset above.
Hope this makes sence and something can provide a solution as I will be most grateful.
Thanks
Ally
|
|

November 9th, 2004, 02:38 PM
|
|
Authorized User
|
|
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
I suggest you break it down into 2 parts:
1. determining the medal standings per event
2. summing up the standings.
1. probably would have to use a temporary variable.
<xsl:variable name="medals">
<medals>
<xsl:for-each select="records">
... so on til you get to event
<xsl:for-each select="player">
<xsl:sort select="record-score">
<player>
<xsl:copy-of select="@name"/>
<xsl:choose>
<xsl:when test="position()=1"><gold/></xsl:when>
<xsl:when test="position()=2"><silver/></xsl:when>
<xsl:when test="position()=3"><bronze/></xsl:when>
...
you need to have some sort of if or choose statement
so that you sort ascending(default) if it's time, and descending (order="descending") if it's points.
2. Counting should be easy: for each player:
variable name="cname" select="@name"
select="count($medals/records/event/player[@name=$cname]/gold"
select="count($medals/records/event/player[@name=$cname]/silver"
select="count($medals/records/event/player[@name=$cname]/bronze"
|
|

November 10th, 2004, 01:36 PM
|
|
Registered User
|
|
Join Date: Nov 2004
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Thanks for your help.
I've tried to create the stylesheet but I'm getting the error
"Expression must evaluate to a node-set" on the
xsl:value-of select="count($medals/records/event/player[@name=...
lines in the code below.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:variable name="medals">
<medals>
<xsl:for-each select="records">
<records>
<xsl:for-each select="event">
<event>
<xsl:for-each select="player">
<xsl:sort select="record-score"/>
<player>
<!-- Does the below copy-of @name build this?
<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute>
--> <xsl:copy-of select="@name"/>
<xsl:choose>
<xsl:when test="position()=1">
<gold/>
</xsl:when>
<xsl:when test="position()=2">
<silver/>
</xsl:when>
<xsl:when test="position()=3">
<bronze/>
</xsl:when>
</xsl:choose>
</player>
</xsl:for-each>
</event>
</xsl:for-each>
</records>
</xsl:for-each>
</medals>
</xsl:variable>
<xsl:variable name="events" select="/games/records/event"/>
<xsl:template match="/">
<html>
<body>
<h2>Medal Table</h2>
<table border="1">
<tr>
<th>Name</th>
<th>Gold</th>
<th>Silver</th>
<th>Bronze</th>
</tr>
<tr>
<td>Player 1</td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player1']/gold)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player1']/silver)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player1']/bronze)"/></td>
</tr>
<tr>
<td>Player 2</td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player2']/gold)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player2']/silver)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player2']/bronze)"/></td>
</tr>
<tr>
<td>Player 3</td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player3']/gold)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player3']/silver)"/></td>
<td><xsl:value-of select="count($medals/records/event/player[@name='Player3']/bronze)"/></td>
</tr>
</table>
<br/>
<xsl:text>Total number of events is </xsl:text>
<xsl:value-of select="count($events)"/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Can you point me in the right direction.
I know I haven't changed the sort order yet depending on the "score" attribute value.
Thanks again
Ally
|
|

November 10th, 2004, 02:23 PM
|
|
Authorized User
|
|
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
I forgot, this only works in XSLT 1.1 and above, (or depends on your processor) because of a change to result-tree fragments not being node-sets. If you change to version 1.1 and the processor accepts it, you're set.
If you have to use XSLT 1.0, I suggest seperating it into the 2 transformations. After the 1st you have a temporary file (what the variable would be). Then you run the 2nd XSLT transformation on the temp file. You probably could leave it as one transformation, but then the logic becomes a lot more complicated, not easily solvable.
|
|

November 11th, 2004, 05:35 AM
|
 |
Wrox Author
|
|
Join Date: Jun 2003
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
|
|
Which XSLT processor are you using? Most have a function, normally called ns:node-set, that returns a node-set from an RTF (result tree fragment).
--
Joe ( Microsoft MVP - XML)
|
|

November 11th, 2004, 01:37 PM
|
|
Registered User
|
|
Join Date: Nov 2004
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Quote:
quote:Originally posted by joefawcett
Which XSLT processor are you using? Most have a function, normally called ns:node-set, that returns a node-set from an RTF (result tree fragment).
--
Joe (Microsoft MVP - XML)
|
I'm using IE6 so which is the minimum version of msxml do I need to install?
I've also looked at http://msdn.microsoft.com/library/de...e_Initiate.asp maybe I'm not using the latest version of msxml that's on my desktop but I need to check.
Thanks for the help
Ally
|
|

November 12th, 2004, 04:51 AM
|
 |
Wrox Author
|
|
Join Date: Jun 2003
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
|
|
If you're using IE with an embedded stylesheet then version 3 is the best you can hope for, it must be installed in replace mode.
It has a node-set function, see the docs.
--
Joe ( Microsoft MVP - XML)
|
|

November 12th, 2004, 01:25 PM
|
|
Registered User
|
|
Join Date: Nov 2004
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
Joe,
I've used the following html via IE to force using the required version of MSXML but it still fails with the same error for versions 3 and 4.
"Expression must evaluate to a node-set" on the
xsl:value-of select="count($medals/records/event/player[@name=....
<HTML>
<HEAD>
<TITLE>sample</TITLE>
<SCRIPT language = "javascript">
function init()
{
var srcTree = new ActiveXObject("Msxml2.DOMDocument.4.0");
srcTree.async=false;
// You can substitute other XML file names here.
srcTree.load("example.xml");
var xsltTree= new ActiveXObject("Msxml2.DOMDocument.4.0");
xsltTree.async = false;
// You can substitute other XSLT file names here.
xsltTree.load("example.xsl");
resTree.innerHTML = srcTree.transformNode(xsltTree);
}
</SCRIPT>
</HEAD>
<BODY onload = "init()" >
<div id="resTree"></div>
</BODY>
</HTML>
The example xml and xsl are from my previous posts. Should I be changing the xsl in some way?
Maybe I should take jkmyoung's advice and use a temporary file.
Ally
|
|

November 12th, 2004, 01:41 PM
|
|
Authorized User
|
|
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
|
|
I want to add a warning about using that method as well: if you actually write the file to disk, performance will be slow because:
1. File IO is slow.
2. You'd have to reparse the tree.
If you use a method that keeps the temporary file in memory, then you won't have that problem.
|
|

November 13th, 2004, 10:45 AM
|
 |
Wrox Author
|
|
Join Date: Jun 2003
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
|
|
Code:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="html"/>
<xsl:key name="players" use="@name" match="player"/>
<xsl:template match="/">
<xsl:variable name="results">
<xsl:apply-templates select="games/records/event"/>
</xsl:variable>
<xsl:variable name="nsResults" select="msxsl:node-set($results)"/>
<table>
<thead>
<tr><th>Name</th><th>Gold</th><th>Silver</th><th>Bronze</th></tr>
</thead>
<tbody>
<xsl:for-each select="games/records/event/player[generate-id() = generate-id(key('players', @name)[1])]">
<xsl:sort data-type="text" order="ascending" select="@name"/>
<tr>
<td><xsl:value-of select="@name"/></td>
<td align="center"><xsl:value-of select="count($nsResults/result[@player = current()/@name and @position = 1])"/></td>
<td align="center"><xsl:value-of select="count($nsResults/result[@player = current()/@name and @position = 2])"/></td>
<td align="center"><xsl:value-of select="count($nsResults/result[@player = current()/@name and @position = 3])"/></td>
</tr>
</xsl:for-each>
</tbody>
</table>
</xsl:template>
<xsl:template match="event">
<xsl:variable name="sortDirection">
<xsl:choose>
<xsl:when test="@score = 'time'">ascending</xsl:when>
<xsl:when test="@score = 'points'">descending</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:apply-templates select="player">
<xsl:sort data-type="number" order="{$sortDirection}" select="record-score"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="player">
<result position="{position()}" player="{@name}"/>
</xsl:template>
</xsl:stylesheet>
Beyond me to do it without using node-set function...
--
Joe ( Microsoft MVP - XML)
|
|
 |