0

I've just started using XSLT and am trying to figure this out. Your help would be greatly appreciated.

Example XML file

<purchases>
    <item id = "1" customer_id = "5">
    </item>
    <item id = "2" customer_id = "5">
    </item>
    <item id = "7" customer_id = "4">
    </item>
</purchases>

Desired HTML Output:

<table>
    <tr><th>Customer ID</th><th>Number of Items Purchased</th></tr>
    <tr><td>5</td><td>2</td></tr>
    <tr><td>4</td><td>1</td></tr>
</table>

Customer with id number 5 has bought 2 items. Customer with id number 4 has bought 1 item.

2
  • Huh? Where do the 5 & 2 in the results come from? 2 is a count? Commented Jun 5, 2012 at 16:07
  • My mistake sorry.Thank you for pointing it out : ) Commented Jun 5, 2012 at 16:10

3 Answers 3

1

XSLT 1.0 solution ....

<?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" indent="yes"/>

      <xsl:template match="/">
        <table>
          <xsl:apply-templates select="purchases/item"/>     
        </table>
      </xsl:template>

   <xsl:template match="item">
     <xsl:if test="not(
       preceding-sibling::*[@customer_id=current()/@customer_id]
       )">     
         <tr><td><xsl:value-of select="@customer_id"/></td>
           <td><xsl:value-of select="count(following-sibling::item
             [@customer_id=current()/@customer_id])+1"/></td>
         </tr> 
     </xsl:if>   
  </xsl:template>

</xsl:stylesheet>
Sign up to request clarification or add additional context in comments.

Comments

0

A possible Xslt 1.0 solution would be

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" indent="yes"/>

    <xsl:key name="kCustomer" match="item" use="@customer_id"/>

    <xsl:template match="/">
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="purchases">
        <xsl:element name="table">
            <xsl:element name="tr">
                <xsl:element name="th">Customer ID</xsl:element>
                <xsl:element name="th">Number of Items Purchased</xsl:element>
                <xsl:for-each select="item[generate-id(.) = generate-id(key('kCustomer', @customer_id))]">
                    <xsl:variable name="total" select="count(/purchases/item[@customer_id=current()/@customer_id]/@id)" />
                    <xsl:element name="tr">
                        <xsl:element name="td">
                            <xsl:value-of select="./@customer_id" />
                        </xsl:element>
                        <xsl:element name="td">
                            <xsl:value-of select="$total" />
                        </xsl:element>
                    </xsl:element>
                </xsl:for-each>
            </xsl:element>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Adopted from Dimitres answer on How can I Group and sort based on sum.


As pointed out by Dimitre the purchases template can be simplified to

    <xsl:template match="purchases">
        <table>
            <tr>
                <th>Customer ID</th>
                <th>Number of Items Purchased</th>
            </tr>
            <xsl:for-each select="item[generate-id(.) = generate-id(key('kCustomer', @customer_id))]">
                <xsl:variable name="total" select="count(/purchases/item[@customer_id=current()/@customer_id]/@id)" />
                    <tr>
                        <td>
                            <xsl:value-of select="./@customer_id" />
                        </td>
                        <td>
                            <xsl:value-of select="$total" />
                        </td>
                    </tr>
            </xsl:for-each>
        </table>
    </xsl:template>

7 Comments

This is good, but why are you using xsl:element and not literal-result elements? The code in its present form is much longer and quite difficult to read/understand.
@DimitreNovatchev I admit the use of xsl:element instead of mixing is just my personal preference. Using it in answers and examples here may have the benefit showing people new to Xslt how construct output. In this case my answers was meant as an complete alternative to Sean and merely an exercise for me since I had to refresh my knowledge from your answer.
@DimitreNovatchev: If your stylesheet is formatted nicely, then the output includes white space between literal-result elements; using pure xsl: elements prevents this. Although in this case, given the indent="yes" in the output method it shouldn't make any difference.
@Filburt: I strongly recommend using literal-result elements always when possible. Personal preferences shouldn't affect readability as the final result is for use of many thousands of people. As for constructing output, the first thing a novice has to learn is the simple use of literal-result elements.
@Flynn1179: Yes, you are absolutely right that xsl:output indent="yes" is a good formattiong solution -- better than using xsl:element for that purpose.
|
0

If you want XSLT 2.0 then the following XSLT should do the trick:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output indent="yes"/>
    <xsl:template match="/purchases">
        <table>
            <tr>
                <th>Customer ID</th>
                <th>Number of Items Purchased</th>
            </tr>
            <xsl:for-each-group select="item" group-by="@customer_id">
                <tr>
                    <td>
                        <xsl:value-of select="current-grouping-key()"/>
                    </td>
                    <td>
                        <xsl:value-of select="count(current-group())"/>
                    </td>
                </tr>
            </xsl:for-each-group>
        </table>
    </xsl:template>
</xsl:stylesheet>

1 Comment

I get header outputs but no data : ( Do you have an XSLT 1.0 solution? Thank you for all your help and formatting my question :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.