Solved

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

Posted on 2004-08-19
2
338 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
Comment Utility
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
Comment Utility
Perfect.  Thanks mate.
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Preface This article introduces an authentication and authorization system for a website.  It is understood by the author and the project contributors that there is no such thing as a "one size fits all" system.  That being said, there is a certa…
Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
Viewers will learn about the different types of variables in Java and how to declare them. Decide the type of variable desired: Put the keyword corresponding to the type of variable in front of the variable name: Use the equal sign to assign a v…
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:

762 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

11 Experts available now in Live!

Get 1:1 Help Now