Solved

Attribute based grouping with XSL

Posted on 2004-04-02
4
271 Views
Last Modified: 2013-11-19
Hi All,

I'm facing a prob lem and can't figure it out. The XML and wanted result is shown below

My XML:
<Results>
      <date/>
      <GROUP Name="G1">
            <ELEMENT name="A">
                  <Value depth="10">5ppm</Value>
                  <Value depth="20">15ppm</Value>
                  <Value depth="30">25ppm</Value>
            </ELEMENT>
            <ELEMENT name="B">
                  <Value depth="10">5ppm</Value>
                  <Value depth="20">15ppm</Value>
                  <Value depth="30">25ppm</Value>
            </ELEMENT>
      </GROUP>
      <GROUP Name="G2">
            <ELEMENT name="C">
                  <Value depth="11">5ppm</Value>
                  <Value depth="21">15ppm</Value>
                  <Value depth="31">25ppm</Value>
            </ELEMENT>
            <ELEMENT name="D">
                  <Value depth="11">5ppm</Value>
                  <Value depth="21">15ppm</Value>
                  <Value depth="31">25ppm</Value>
            </ELEMENT>
      </GROUP>
</Results>

Wanted result:

Table:
Group G1
              A           B
Depth
10          5            5
20          15         15
30          25         25

Group G2
              C           D
Depth
11          5            5
21          15         15
31          25         25

....
0
Comment
Question by:krisnackaerts
  • 2
4 Comments
 
LVL 7

Expert Comment

by:culshaja
ID: 10740151
Hi,

I got this from the XSL FAQ on http://www.dpawson.co.uk:

Here's a stylesheet that produces the cross-product you're looking for. It uses an <xsl:key> like functional indexes to speed up the iteration of unique regions.

Assuming the input is:

<demo>
  <salesman id="0001" name="Rick Peterson">
    <account id="act001" region="Midwest">Johnosn's Laundry</account>
    <account id="act002" region="Canada">Franks's Laundry</account>
    <account id="act003" region="Alaska">Mary's Laundry</account>
    <account id="act004" region="New Jersey">Bill's Laundry</account>
    <account id="act005" region="Midwest">Hammond's Laundry</account>
  </salesman>
  <salesman id="0003" name="Ty Coon">
    <account id="act006" region="Canada">Franks's Diner</account>
    <account id="act007" region="Midwest">Johnosn's Diner</account>
    <account id="act008" region="Canada">Hammond's Diner</account>
    <account id="act009" region="Alaska">Mary's Diner</account>
    <account id="act010" region="Alaska">Bill's Diner</account>
  </salesman>
</demo>

The following stylesheet produces an HTML table crosstab with salespeople down the left (ordered by name) and regions across the top (ordered by name), with a grid cell for each crosstab, and &nsbp; for empty cells.

<!-- Example of a "CrossTab" stylesheet
       using <xsl:key>'s for grouping -->
<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="b" match="@region" use="."/>
  <xsl:template match="/">
    <html>  
      <body>
        <table border="1" cellspacing="0">
          <!-- Generate the row of table header cells -->
          <tr>
            <th>Salesperson</th>
            <!--
             | Generate a header cell for each unique region name
             |
             | See Chapter 9 of "Building Oracle XML Applications"
                                 from O'Reilly
             | for a detailed explanation of how this
                  <xsl:key> based technique works
             +-->
            <xsl:for-each
        select="//@region[generate-id(.)=generate-id(key('b',.)[1])]">
              <!-- Sort by the region name
                   (the value of the current @region attribute -->
              <xsl:sort select="."/>
              <th>
                <xsl:value-of select="."/>
              </th>
            </xsl:for-each>
          </tr>
        <!-- Generate a row for each salesman -->
        <xsl:for-each select="demo/salesman">
          <!-- Sort by salesman name -->
          <xsl:sort select="@name"/>
          <!-- Keep the current salesman in a variable for later -->
          <xsl:variable name="cur" select="."/>
          <tr>
            <!-- First cell has the salesman's name -->
            <td bgcolor="yellow">
                 <xsl:value-of select="@name"/></td>
            <!-- Generate a cell for each unique region -->
            <xsl:for-each
             select="//@region[generate-id(.)=
                    generate-id(key('b',.)[1])]">
              <td>
                <!-- If no accts for current salesman in current region,
                   do &nbsp; -->
                <xsl:if
                  test="not($cur/account[@region=current()])">
                        &#160;</xsl:if>
               
<!-- List matching accounts for current salesman in current region -->
                <xsl:for-each select="$cur/account[@region=current()]">
                  <xsl:value-of select="."/>
                  <xsl:if test="position() != last()"><br/></xsl:if>
                </xsl:for-each>
              </td>
            </xsl:for-each>
          </tr>
        </xsl:for-each>
      </table>
    </body>
  </html>
  </xsl:template>
</xsl:stylesheet>


James :-)
0
 

Author Comment

by:krisnackaerts
ID: 10740724
Thank's for the info, though,

I've been experimenting with xsl:key and use it actually in a similar way. The problem here is that the key is calculated for the whole document, and I need it only to be applied at a specific node. When I apply code similar to that above, I would get for depth:
10
11
20
21
30
31

I hope I made my problem clear, I was thinking more of using variables now...?

Kris
0
 
LVL 5

Accepted Solution

by:
conorj earned 250 total points
ID: 10742294
Is this what you want??

..................
<xsl:template match="/">
    <table>
        <xsl:apply-templates select="Results/GROUP" />
    </table>
</xsl:template>

<xsl:template match="GROUP">
    <tr>
        <th align="left" colspan="3">Group <xsl:value-of select="@Name" /></th>
    </tr>
    <tr>
        <th>Depth</th>
        <th><xsl:value-of select="ELEMENT[1]/@name" /></th>
        <th><xsl:value-of select="ELEMENT[2]/@name" /></th>
    </tr>
    <xsl:apply-templates select="ELEMENT[1]/Value" />
</xsl:template>

<xsl:template match="Value">
    <tr>
        <td>&#160;</td>
        <td><xsl:value-of select="." /></td>
        <td><xsl:value-of select="../following-sibling::ELEMENT/Value[@depth = current()/@depth]" /></td>
    </tr>
</xsl:template>
...........

HTH.

rgds,
Conor.
0
 

Author Comment

by:krisnackaerts
ID: 10926311
Many thank's,
The last solution was partially correct but helped me a lot.

<xsl:template match="XXXX">
        <xsl:apply-templates select="Results/GROUP"/>
</xsl:template>

<xsl:template match="GROUP">
        <DIV CLASS='GROUP'><xsl:value-of select="@name"/></DIV>
        <TABLE CLASS='GROUP'>
                <TR>
                        <TD CLASS='HEADERx'>depth</TD>
                        <xsl:for-each select="ELEMENT">
                                <TD CLASS='HEADERx'><xsl:value-of select="@name" /></TD>
                        </xsl:for-each>
                </TR>
                <xsl:apply-templates select="ELEMENT[1]/Value" />
        </TABLE>
</xsl:template>


<xsl:template match="Value">
        <TR>
                <TD class="Datax"><xsl:value-of select="./@depth"/></TD>
                <TD class="Datax"><xsl:value-of select="."/>x</TD>
                <xsl:for-each select="../following-sibling::ELEMENT/Value[@depth= current()/@depth]">
                        <TD class="Datax"><xsl:value-of select="."/></TD>
                </xsl:for-each>
        </TR>
</xsl:template>
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Browsers only know CSS so your awesome SASS code needs to be translated into normal CSS. Here I'll try to explain what you should aim for in order to take full advantage of SASS.
JavaScript has plenty of pieces of code people often just copy/paste from somewhere but never quite fully understand. Self-Executing functions are just one good example that I'll try to demystify here.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …
Learn how to create flexible layouts using relative units in CSS.  New relative units added in CSS3 include vw(viewports width), vh(viewports height), vmin(minimum of viewports height and width), and vmax (maximum of viewports height and width).

895 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now