Solved

XSLT: Loop through elements, displaying header when attribute value changes

Posted on 2004-08-19
2
340 Views
Last Modified: 2013-11-19
My XML:

<?xml version="1.0" encoding="utf-8" ?>
<PhoneBill>
    <CallDetails>
        <Call type="Local" date="01-Jan-2001" destination="Brisbane" numberDialed="0755557896" duration="1:53" rate="Flat" charge="0.23"></Call>
        <Call type="Local" date="02-Jan-2001" destination="Brisbane" numberDialed="0755557896" duration="0:42" rate="Flat" charge="0.23"></Call>
        <Call type="Local" date="02-Jan-2001" destination="Brisbane" numberDialed="0755551234" duration="8:65" rate="Flat" charge="0.23"></Call>
        <Call type="Local" date="05-Feb-2001" destination="Brisbane" numberDialed="0755557896" duration="1:02" rate="Flat" charge="0.23"></Call>
        <Call type="Local" date="02-Mar-2001" destination="Brisbane" numberDialed="0755551265" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="International" date="02-Mar-2001" destination="Somewhere" numberDialed="41117555126" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="International" date="03-Mar-2001" destination="Somewhere" numberDialed="41117555126" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="International" date="04-Mar-2001" destination="Somewhere" numberDialed="41117555126" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="International" date="05-Mar-2001" destination="Somewhere" numberDialed="41117555126" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="02-Jun-2001" destination="National" numberDialed="0455551243" duration="0:10" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="02-Feb-2001" destination="National" numberDialed="0455551254" duration="0:14" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="03-Feb-2001" destination="National" numberDialed="0455551265" duration="0:19" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="06-Feb-2001" destination="National" numberDialed="0455551263" duration="5:21" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="08-Feb-2001" destination="National" numberDialed="0455551265" duration="0:23" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="12-Feb-2001" destination="National" numberDialed="0455551265" duration="3:11" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="27-Feb-2001" destination="National" numberDialed="0455551234" duration="0:20" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="12-Mar-2001" destination="National" numberDialed="0455551265" duration="0:14" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="22-Mar-2001" destination="National" numberDialed="0455551234" duration="0:16" rate="Flat" charge="0.23"></Call>
        <Call type="Mobile" date="26-Mar-2001" destination="National" numberDialed="0455551265" duration="1:13" rate="Flat" charge="0.23"></Call>
    </CallDetails>
</PhoneBill>

I want to have a XSLT file that will provide output for each Call, but display a header for each group of unique @type and also a count of calls per @type and sum() of @charges for that group.

Required output (I haven't bothered including the <tr> and <td> tags:

<b>Local Calls</b>
<p>
Date            Destination  Number Dialed  Duration  Rate  Charge
</p>
<p>
01-Jan-2001 Brisbane     0755557896      1:53       Flat    $0.23
02-Jan-2001 Brisbane     0755557896      0:42       Flat    $0.23
02-Jan-2001 Brisbane     0755557896      8:65       Flat    $0.23
05-Feb-2001 Brisbane     0755557896      1:02       Flat    $0.23
02-Mar-2001 Brisbane     0755557896      0:10       Flat    $0.23
</p>
<p>
Number Local Calls:  5
Total Local Charges:  $1.15
</p>
<b>International Calls</b>
<p>
Date            Destination  Number Dialed  Duration  Rate  Charge
</p>
<p>
01-Jan-2001 Somewhere     41117555126      1:53       Flat    $0.23
02-Jan-2001 Somewhere     41117555126      0:42       Flat    $0.23
02-Jan-2001 Somewhere     41117555126      8:65       Flat    $0.23
05-Feb-2001 Somewhere     41117555126      1:02       Flat    $0.23
</p>
<p>
Number Local Calls:  4
Total Local Charges:  $1.15
</p>

--------------

and so on....

My XLST file thus far is:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN"/>
    <xsl:template match="//PhoneBill">
        <html>
            <head>
            </head>
            <body>
                <font face="arial" size="2">
                    <xsl:for-each select="CallDetails/Call">
                        <h3><xsl:value-of select="."/> Calls</h3>
                        <p>
                        Date: <xsl:value-of select="/./@date"/><br />
                        Destination: <xsl:value-of select="//./@date"/><br />
                        Number Dialed: <xsl:value-of select="//./@numberDialed"/><br />
                        Duration: <xsl:value-of select="//./@duration"/><br />
                        Rate: <xsl:value-of select="//./@rate"/><br />
                        Charge: $<xsl:value-of select="//./@charge"/>
                        </p>
                        <b>
                            Number of calls: <xsl:value-of select="count(.)"/><br />
                            Total Charges: $<xsl:value-of select="sum(./@charge)"/>
                        </b>
                    </xsl:for-each>
                </font>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

I had intended to next another for-each statement to display each Call, but the concept isn't working as I had expected.  I had initially tried to have the outer for-each condition to be "CallDetails/Call/@type" in the hope that would determine when the @type attribute value changed, but not so.

Could you please provide XSLT code to output what I'm after or, if the XML format has to be changed to rather have child nodes for each value rather than attributes, please let me know.  <-- I was thinking that maybe by using nodes, I could use preceding-sibling, etc to reference the correct values.

This is my second day having a crack at XSLT and am running out of hair to pull out!  :)
0
Comment
Question by:gadkins
2 Comments
 
LVL 5

Accepted Solution

by:
conorj earned 200 total points
ID: 11839550
Hi there,

You need to us the muenchian grouping method. This allows you to group elements according to some value ( in thie case the @type). I'va placed a modified version of your XSLT below.

rgds,
Conor.

XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN"/>
    <xsl:key name="calls" match="Call" use="@type" />
    <xsl:template match="PhoneBill">
        <html>
            <head></head>
            <body>
                <font face="arial" size="2">
                    <xsl:for-each select="CallDetails/Call[count(.|key('calls', @type)[1]) = 1]">
                        <xsl:variable name="type" select="@type" />
                        <h3><xsl:value-of select="@type"/> Calls</h3>
                        <xsl:for-each select="../Call[@type = $type]">
                            <p>
                            Date: <xsl:value-of select="@date"/><br />
                            Destination: <xsl:value-of select="@date"/><br />
                            Number Dialed: <xsl:value-of select="@numberDialed"/><br />
                            Duration: <xsl:value-of select="@duration"/><br />
                            Rate: <xsl:value-of select="@rate"/><br />
                            Charge: $<xsl:value-of select="@charge"/>
                            </p>
                        </xsl:for-each>
                        <b>
                            Number of calls: <xsl:value-of select="count(key('calls', $type))"/><br />
                            Total Charges: $<xsl:value-of select="sum(key('calls', $type)/@charge)"/>
                        </b>
                    </xsl:for-each>
                </font>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>
0
 
LVL 2

Author Comment

by:gadkins
ID: 11848227
Perfect.  Thanks mate.
0

Featured Post

3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
XML Parsing Classic ASP 5 57
Online tools to transform xml to excel using xsl 1 42
.php tree directory? 5 73
configure dependency in POM for new database 3 25
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…
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 about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:
HTML5 has deprecated a few of the older ways of showing media as well as offering up a new way to create games and animations. Audio, video, and canvas are just a few of the adjustments made between XHTML and HTML5. As we learned in our last micr…

777 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