Solved

Attribute based grouping with XSL

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

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
XSLT XML 4 32
tutorial for ebay api 3 74
Add to XML (Powershell) 1 41
Customizing XML with Powershell 2 31
Browsing the questions asked to the Experts of this forum, you will be amazed to see how many times people are headaching about monster regular expressions (regex) to select that specific part of some HTML or XML file they want to extract. The examp…
The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.

679 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