XSL 1.0 grouping

Viewed 35

I need some help. I am desperate with this transformation. I must use XSLT 1.0.

I have this input:

<?xml version="1.0" encoding="utf-8" ?>
<export>
    <ds>
        <cds>
            <cd id_c="1">
                <cs id="123" >
                    <acc>
                        <payments>
                            <payment id_p="9" price="6.90"/>
                        </payments>
                    </acc>
                    <acc>
                        <payments>
                            <payment id_p="9" price="6.50"/>
                        </payments>
                    </acc>
                </cs>
            </cd>
            <cd id_c="2">
                <cs id="456" >
                    <acc>
                        <payments>
                            <payment id_p="1" price="4.30"/>
                        </payments>
                    </acc>
                    <acc>
                        <payments>
                            <payment id_p="9" price="11.20"/>
                        </payments>
                    </acc>
                    <acc>
                        <payments>
                            <payment id_p="1" price="2.10"/>
                        </payments>
                    </acc>
                </cs>
            </cd>
            <cd id_c="3">
                <cs id="789" >
                    <acc>
                        <payments>
                            <payment id_p="2" price="8.90"/>
                        </payments>
                    </acc>
                    <acc>
                        <payments>
                            <payment id_p="9" price="5.70"/>
                        </payments>
                    </acc>
                </cs>
            </cd>           
        </cds>
    </ds>
</export>

and I created this transformation

    <xsl:key name="group_payment" match="acc/payments" use="payment/@id_p"/>

    <xsl:template match="export">
    <document>
      <xsl:for-each select=".//ds/cds/cd">
            <record>
                <header>
                    <number><xsl:value-of select=".//@id" /></number>
                </header>
                <items>
                    <xsl:for-each select=".//payments[generate-id(.)=generate-id(key('group_payment', payment/@id_p))]/payment/@id_p">
                    <xsl:sort select="." order="ascending" case-order="lower-first" />
                    <item>
                        <text>Payment ID: <xsl:value-of select="."/></text>
                        <price><xsl:value-of select="sum(key('group_payment', .)//@price)"/></price>
                    </item>
                    </xsl:for-each>
                </items>
            </record>

        </xsl:for-each>
    </document>
    </xsl:template>

But this didn't work good for me. It groups every payment to "first match". I need to group payments by payment/@id_p inside every <cd> element.

This my output:

<?xml version="1.0" encoding="utf-8"?>
<document>
    <record>
        <header>
            <number>123</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 9</text>
                <price>30.3</price>
            </item>
        </items>
    </record>
    <record>
        <header>
            <number>456</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 1</text>
                <price>6.4</price>
            </item>
        </items>
    </record>
    <record>
        <header>
            <number>789</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 2</text>
                <price>8.9</price>
            </item>
        </items>
    </record>
</document>

and I want this output:

<?xml version="1.0" encoding="utf-8"?>
<document>
    <record>
        <header>
            <number>123</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 9</text>
                <price>13.40</price>
            </item>
        </items>
    </record>
    <record>
        <header>
            <number>456</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 1</text>
                <price>6.40</price>
            </item>
            <item>
                <text>Payment ID: 9</text>
                <price>11.20</price>
            </item>
            </items>
    </record>
    <record>
        <header>
            <number>789</number>
        </header>
        <items>
            <item>
                <text>Payment ID: 2</text>
                <price>8.90</price>
            </item>
            <item>
                <text>Payment ID: 9</text>
                <price>5.70</price>
            </item>
        </items>
    </record>
</document>

I tried a different grouping, but it always ends with same result. Can someone help me please?

1 Answers

AFAICT, you want to do:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="pmt" match="payment" use="concat(@id_p, '|', ancestor::cs/@id)"/>

<xsl:template match="export">
    <document>
        <xsl:for-each select="ds/cds/cd/cs">
            <xsl:variable name="cs_id" select="@id" />
            <record>
                <header>
                    <number>
                        <xsl:value-of select="$cs_id" />
                    </number>
                </header>
                <items>
                    <xsl:for-each select="acc/payments/payment[generate-id() = generate-id(key('pmt', concat(@id_p, '|', $cs_id))[1])]">
                        <item>
                            <text>
                                <xsl:text>Payment ID: </xsl:text>
                                <xsl:value-of select="@id_p"/>
                            </text>
                            <price>
                                <xsl:value-of select="sum(key('pmt', concat(@id_p, '|', $cs_id))/@price)"/>
                            </price>
                        </item>
                    </xsl:for-each>
                </items>
            </record>
        </xsl:for-each>
    </document>
</xsl:template>

</xsl:stylesheet>
Related