?
Solved

Attribute based grouping with XSL

Posted on 2004-04-02
4
Medium Priority
?
307 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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 750 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

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Question has a verified solution.

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

I will show you how to create a ASP.NET Captcha control without using any HTTP HANDELRS or what so ever. you can easily plug it into your web pages. For Example a = 2 + 3 (where 2 and 3 are 2 random numbers) Session("Answer") = 5 then we…
What is Node.js? Node.js is a server side scripting language much like PHP or ASP but is used to implement the complete package of HTTP webserver and application framework. The difference is that Node.js’s execution engine is asynchronous and event…
Viewers will learn about arithmetic and Boolean expressions in Java and the logical operators used to create Boolean expressions. We will cover the symbols used for arithmetic expressions and define each logical operator and how to use them in Boole…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Suggested Courses

741 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