Solved

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

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

Transaction Monitoring Vs. Real User Monitoring

Synthetic Transaction Monitoring Vs. Real User Monitoring: When To Use Each Approach? In this article, we will discuss two major monitoring approaches: Synthetic Transaction and Real User Monitoring.

Question has a verified solution.

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

Styling your websites can become very complex. Here I'll show how SASS can help you better organize, maintain and reuse your CSS code.
JavaScript has plenty of pieces of code people often just copy/paste from somewhere but never quite fully understand. Self-Executing functions are just one good example that I'll try to demystify here.
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …
Learn how to create flexible layouts using relative units in CSS.  New relative units added in CSS3 include vw(viewports width), vh(viewports height), vmin(minimum of viewports height and width), and vmax (maximum of viewports height and width).

726 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