Solved

Attribute based grouping with XSL

Posted on 2004-04-02
4
304 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 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

DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

Question has a verified solution.

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

Preface In the first article: A Better Website Login System (http://www.experts-exchange.com/A_2902.html) I introduced the EE Collaborative Login System and its intended purpose. In this article I will discuss some of the design consideratio…
I found this questions asking how to do this in many different forums, so I will describe here how to implement a solution using PHP and AJAX. The logical flow for the problem should be: Write an event handler for the first drop down box to get …
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

724 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