Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
| Search | Today's Posts | Mark Forums Read
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
 
Old November 9th, 2004, 12:13 PM
Registered User
 
Join Date: Nov 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default 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
 
Old November 9th, 2004, 02:38 PM
Authorized User
 
Join Date: Nov 2004
Location: , , .
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

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"
 
Old November 10th, 2004, 01:36 PM
Registered User
 
Join Date: Nov 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

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
 
Old November 10th, 2004, 02:23 PM
Authorized User
 
Join Date: Nov 2004
Location: , , .
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

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.

 
Old November 11th, 2004, 05:35 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

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)
 
Old November 11th, 2004, 01:37 PM
Registered User
 
Join Date: Nov 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

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

 
Old November 12th, 2004, 04:51 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

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)
 
Old November 12th, 2004, 01:25 PM
Registered User
 
Join Date: Nov 2004
Location: , , .
Posts: 6
Thanks: 0
Thanked 0 Times in 0 Posts
Default

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

 
Old November 12th, 2004, 01:41 PM
Authorized User
 
Join Date: Nov 2004
Location: , , .
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

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.

 
Old November 13th, 2004, 10:45 AM
joefawcett's Avatar
Wrox Author
Points: 9,763, Level: 42
Points: 9,763, Level: 42 Points: 9,763, Level: 42 Points: 9,763, Level: 42
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jun 2003
Location: Exeter, , United Kingdom.
Posts: 3,074
Thanks: 1
Thanked 38 Times in 37 Posts
Default

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)




Similar Threads
Thread Thread Starter Forum Replies Last Post
creating table ph0neman Classic ASP Basics 3 January 22nd, 2008 05:06 PM
Ch. 4 -1. Creating a Table jfr BOOK: Beginning PHP, Apache, MySQL Web Development ISBN: 978-0-7645-5744-6 2 February 16th, 2005 02:26 PM
creating a table on server2 FALCONSEYE SQL Server 2000 3 January 26th, 2005 03:16 PM
Creating table with existing table without value kumar_kumar Oracle 1 January 4th, 2005 07:12 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.