Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

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

Posted on 2004-08-19
2
Medium Priority
?
347 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 800 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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

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

This article covers the basics of the Sass, which is a CSS extension language. You will learn about variables, mixins, and nesting.
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
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…
Suggested Courses

636 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