xsl-list
[Top] [All Lists]

RE: [xsl] Tabluation via XSLT.

2007-04-05 05:54:59

Thanks, Michael.

Without wanting to appear obsequious, I found part of
the solution in your book!  ;-)

I've changed the XSL to firstly make the change you
suggested below and then to adjust the earlier loop
that generates the column headings to make use of
keys, as per an adaptation of the "Muenchian grouping"
method.

Here's the new XSL.  Same output, but now the document
context is retained; ergo I can apply modes to the
formatting of the column headers.

Very slightly clunky still, as I have to maintain two
indexes.  I can't seem to remove the need for
"col-items" and generate "$cols" directly from
"col-nodes", but will probably live with it.

[code]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:fo="http://www.w3.org/1999/XSL/Format";>
        <xsl:output method="html"/>
        <xsl:key name="col-items"
match="container/item/value/item/value/@name"
use="."/>
        <xsl:key name="col-nodes"
match="container/item/value/item/value" use="@name"/>
        <xsl:variable name="cols"
select="container/item/value/item/value/@name[generate-id()=generate-id(key('col-items',.))]"/>
        <xsl:template match="/">
                <html>
                        <head>
                                <title>TEST CODE FOR TABULATION</title>
                        </head>
                        <body>
                                <table>
                                        <tr>
                                                <td class="rowtitle"/>
                                                <!-- set the column headings -->
                                                <xsl:for-each
select="container/item/value/item/value[generate-id()
= generate-id(key('col-nodes', @name)[1])]">
                                                        <xsl:sort 
select="@name" />
                                                        <td>
                                                                <xsl:value-of 
select="@name" />
                                                        </td>
                                                </xsl:for-each>
                                        </tr>
                                        <!-- create the rows -->
                                        <xsl:apply-templates
select="container/item/value">
                                                <xsl:sort/>
                                        </xsl:apply-templates>
                                </table>
                        </body>
                </html>
        </xsl:template>
        <xsl:template match="container/item/value">
                <tr>
                        <td class="rowtitle">
                                <xsl:value-of select=".//@name"/>
                        </td>
                        <!-- squirrel away the tree of ratings under the
row currently being processed -->
                        <xsl:variable name="col_rating"
select="item/value"/>
                        <!-- display the rating for each col (the latter
sorted in line with the column headings above) -->
                        <xsl:for-each select="$cols">
                                <xsl:sort/>
                                <td>
                                        <xsl:for-each
select="$col_rating[(_at_)name=current()]">
                                                <xsl:value-of select="@rating"/>
                                        </xsl:for-each>
                                </td>
                        </xsl:for-each>
                </tr>
        </xsl:template>
</xsl:stylesheet>
[/code]

J.


--- Michael Kay <mike(_at_)saxonica(_dot_)com> wrote:

Your basic approach doesn't seem wrong to me (or
procedural). But the code
can be simplified a lot, for example I think you can
replace:

     <xsl:variable name="current_col" select="."/>
     <!-- only choose the ratings where it exists
against the row currently
being processed -->
     <xsl:for-each select="$col_rating">
      <xsl:choose>
       <xsl:when test=".//@name=$current_col">
        <xsl:value-of select=".//@rating"/>
       </xsl:when>
      </xsl:choose>
     </xsl:for-each>

by

<xsl:for-each select="$col_rating[(_at_)name=current()]">
  <xsl:value-of select="@rating"/>
</xsl:for-each>

Michael Kay
http://www.saxonica.com/ 

-----Original Message-----
From: Furry Pop [mailto:furrypop(_at_)yahoo(_dot_)com] 
Sent: 04 April 2007 09:55
To: xsl-list(_at_)lists(_dot_)mulberrytech(_dot_)com
Subject: [xsl] Tabluation via XSLT.

Hi,
 
I've got an XML document that I wish to convert
into an HTML 
table.  It requires further transformation by some
third 
party XSL libraries, which I will include in my
XSLT and call 
their templates with modes to do their clever
stuff.
 
My approach so far has been rather procedural, and
I have had 
to use variables, etc to maintain state.  For
instance, I use 
a key to remove duplicates from the items to form
the column 
headings and then cycle through each row, matching
against 
the XML via the key.  Each row is sorted in order
to keep it 
aligned with the column headings. The problem with
this is 
that, particularly with the column headers, I lose
sight of 
the original XML structure, thereby preventing me
from 
applying further templates and using the mode
functionality.
 
Is there a more declarative approach to this
processing 
wherein I can build the column headings without
using 
variables and then populate each row in the
knowledge that 
the appropriate item is being inserted into the
appropriate 
column?  I'm thinking of some better use of
apply-templates 
or some clever recursion but I can't see how to do
it.
 
A much simplified version of source file and my
sample XSLT 
look something like the following:-
 
SOURCE FILE:-
[code]
<?xml version="1.0" encoding="UTF-8"?>
<container>
 <item>
  <value name="arthur">
   <item>
    <value rating="X" name="joyce"/>
    <value rating="W" name="ida"/>
   </item>
  </value>
 </item>
 <item>
  <value name="bertrand">
   <item>
    <value rating="Y" name="ida"/>
   </item>
  </value>
 </item>
 <item>
  <value name="colin">
   <item>
    <value rating="Z" name="joyce"/>
   </item>
  </value>
 </item>
 <item>
  <value name="derek">
   <item>
    <value rating="X" name="joyce"/>
   </item>
  </value>
 </item>
 <item>
  <value name="edward">
   <item>
    <value rating="Y" name="hilda"/>
    <value rating="W" name="joyce"/>
   </item>
  </value>
 </item>
 <item>
  <value name="frederick">
   <item>
    <value rating="Z" name="ida"/>
   </item>
  </value>
 </item>
</container>
[/code]
 
XSLT:-
[code]
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:fo="http://www.w3.org/1999/XSL/Format";>
 <xsl:output method="html"/>
 <xsl:key name="distinct-col"
match="container/item/value/item/value/@name"
use="."/>
 <!-- store unique (ie "distinct") column headings
for use 
later-->  <xsl:variable name="cols"


select="container/item/value/item/value/@name[generate-id()=ge
nerate-id(key('distinct-col',.))]"/>
 <xsl:template match="/">
  <html>
   <head>
    <title>TEST CODE FOR TABULATION</title>
   </head>
   <body>
    <table>
     <tr>
      <td class="rowtitle" />
      <!-- set the column headings -->
      <xsl:for-each select="$cols">
       <xsl:sort/>
       <td class="coltitle">
        <xsl:value-of select="."/>
       </td>
      </xsl:for-each>
     </tr>
     <!-- create the rows -->
     <xsl:apply-templates
select="container/item/value">
      <xsl:sort/>
     </xsl:apply-templates>
    </table>
   </body>
  </html>
 </xsl:template>
 <xsl:template match="container/item/value">
  <tr>
   <td class="rowtitle">
    <xsl:value-of select=".//@name"/>
   </td>
   <!-- squirrel away the tree of ratings under
the row 
currently being processed -->
   <xsl:variable name="col_rating"
select="item/value"/>
   <!-- display the rating for each col (the
latter sorted in 
line with the column headings above) -->
   <xsl:for-each select="$cols">
    <xsl:sort/>
    <td>
     <!-- squirrel away the col currently being
viewed
-->
     <xsl:variable name="current_col" select="."/>
     <!-- only choose the ratings where it exists
against the 
row currently being processed -->
     <xsl:for-each select="$col_rating">
      <xsl:choose>
       <xsl:when test=".//@name=$current_col">
        <xsl:value-of select=".//@rating"/>
       </xsl:when>
      </xsl:choose>
     </xsl:for-each>

=== message truncated ===


  ________________________________________________ 
Always trying to pin me down. 
Why a title for your sound?




                
___________________________________________________________ 
What kind of emailer are you? Find out today - get a free analysis of your 
email personality. Take the quiz at the Yahoo! Mail Championship. 
http://uk.rd.yahoo.com/evt=44106/*http://mail.yahoo.net/uk 

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--