Solved

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

Posted on 2004-08-19
2
343 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
[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 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

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Suggested Solutions

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…
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 receive an overview of the basics of CSS showing inline styles. In the head tags set up your style tags: (CODE) Reference the nav tag and set your properties.: (CODE) Set the reference for the UL element and styles for it to ensu…
The viewer will learn the benefit of using external CSS files and the relationship between class and ID selectors. Create your external css file by saving it as style.css then set up your style tags: (CODE) Reference the nav tag and set your prop…

749 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