tesmc
asked on
XSL: How to group and sum into one node
I have the following input:
I want to group by FOP so that my output sums the total:
I was trying to do something like this but it failed:
<xsl:key name="paymentCash" match="stl:Payment" use="stl:FormOfPayment/stl :Cash"/>
<xsl:for-each select="stl:Payment[stl:Fo rmOfPaymen t/stl:Cash and generate-id(.)=generate-id (key('paym entCash', stl:FormOfPayment/stl:Cash )[1])]" >
....
but it would never enter the for-each clause.
<root>
<CollectMiscFeeRQ>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">549</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">549</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">549</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
</CollectMiscFeeRQ>
</root>
I want to group by FOP so that my output sums the total:
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">1647</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
I was trying to do something like this but it failed:
<xsl:key name="paymentCash" match="stl:Payment" use="stl:FormOfPayment/stl
<xsl:for-each select="stl:Payment[stl:Fo
....
but it would never enter the for-each clause.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:stl="http://services.sabre.com/STL/v01"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:key name="paymentCash" match="stl:Payment" use="local-name(stl:FormOfPayment/*)"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="stl:Payment[not(stl:FormOfPayment/stl:Cash)]"></xsl:template>
<xsl:template match="stl:Payment[not(generate-id() = generate-id(key('paymentCash', 'Cash')[1]))]"/>
<xsl:template match="stl:Payment/stl:Amount">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:value-of select="sum(key('paymentCash', 'Cash')/stl:Amount)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
As you can see I use a key based on the name rather than the element (that is I hope I got the requirements right)
You don't need grouping, just phase out the Payments you don't need
- all non cash payments
- all cash payments except the first
(two templates for that)
And calculate the total amount at the level of the amount
(it will only reach the amount once, in the first cash payment)
This way you use the power of the template rules to its maximum, no for each, no if or when, clean maintainable code
This works if you don't want to do something with other payment types
(if there is only one extra payment type, known from the schema, just add another template,
if there are many or if the schema is loose, go for grouping)
If you use XSLT2, use a different type of grouping using for-each-group
Note that if you need to deal with totals on different currencies, you will need grouping
The heart of the issue was the key not looking for the payment type element name.
You can build on that
I gave you the best solution possible with the very limited info you gave... clear specs always help
You don't need grouping, just phase out the Payments you don't need
- all non cash payments
- all cash payments except the first
(two templates for that)
And calculate the total amount at the level of the amount
(it will only reach the amount once, in the first cash payment)
This way you use the power of the template rules to its maximum, no for each, no if or when, clean maintainable code
This works if you don't want to do something with other payment types
(if there is only one extra payment type, known from the schema, just add another template,
if there are many or if the schema is loose, go for grouping)
If you use XSLT2, use a different type of grouping using for-each-group
Note that if you need to deal with totals on different currencies, you will need grouping
The heart of the issue was the key not looking for the payment type element name.
You can build on that
I gave you the best solution possible with the very limited info you gave... clear specs always help
ASKER
@Gertone (Geert Bormans) - yes - need to group on the form of payment.
yes - there would be other forms of payment expressed in the element name. in fact there could be duplicate instances of credit card and those need to be grouped together accordingly.
today i'm grouping and summing amount by credit card successfully it's just for cash that my code fails.
This is what i have for credit card:
yes - there would be other forms of payment expressed in the element name. in fact there could be duplicate instances of credit card and those need to be grouped together accordingly.
today i'm grouping and summing amount by credit card successfully it's just for cash that my code fails.
This is what i have for credit card:
<xsl:key name="paymentKey" match="stl:Payment" use="stl:FormOfPayment/stl:CreditCard/stl:Number"/>
<!-- group by distinct CreditCard/Number -->
<xsl:for-each select="stl:Payment[stl:FormOfPayment/stl:CreditCard/stl:Number and generate-id(.)=generate-id(key('paymentKey', stl:FormOfPayment/stl:CreditCard/stl:Number)[1])]" >
...
OK, so here you group on the creditcard number.
Could you post a less obvious XML that is closer to reality
(different methods of payment, different currencies if you have them, multiple cards, type of cards...)
and handcraft the expected result
Your last message shows it is more complex, but it does not give a clue what you have to do with the complexity
Anyhow, the way I use the key in my first example should hint you towards a solution
Could you post a less obvious XML that is closer to reality
(different methods of payment, different currencies if you have them, multiple cards, type of cards...)
and handcraft the expected result
Your last message shows it is more complex, but it does not give a clue what you have to do with the complexity
Anyhow, the way I use the key in my first example should hint you towards a solution
ASKER
I could have the following input:
and this is expected output:
<CollectMiscFeeRQ>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">200</Amount>
<FormOfPayment>
<CreditCard>
<Code>CA</Code>
<Number>5555444433331111</Number>
<ExpiryDate>1020</ExpiryDate>
<ApprovalCode>36152</ApprovalCode>
</CreditCard>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">200</Amount>
<FormOfPayment>
<CreditCard>
<Code>CA</Code>
<Number>5555444433331111</Number>
<ExpiryDate>1020</ExpiryDate>
<ApprovalCode>36152</ApprovalCode>
</CreditCard>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">200</Amount>
<FormOfPayment>
<CreditCard>
<Code>CA</Code>
<Number>5555444433331111</Number>
<ExpiryDate>1020</ExpiryDate>
<ApprovalCode>36152</ApprovalCode>
</CreditCard>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">100</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">100</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">100</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
</CollectMiscFeeRQ>
and this is expected output:
<CollectMiscFeeRQ>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">600</Amount>
<FormOfPayment>
<CreditCard>
<Code>CA</Code>
<Number>5555444433331111</Number>
<ExpiryDate>1020</ExpiryDate>
<ApprovalCode>36152</ApprovalCode>
</CreditCard>
</FormOfPayment>
</Payment>
<Payment xmlns="http://services.sabre.com/STL/v01">
<Amount currencyCode="MXN">300</Amount>
<FormOfPayment>
<Cash/>
</FormOfPayment>
</Payment>
</CollectMiscFeeRQ>
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Added a second key
I do a first level grouping to differentiate between cash and card
For the card payments I do a second level grouping for each card number
Rest flows nicely through the templates
If you have done Muenchian Grouping before, I guess this should be clear
I do a first level grouping to differentiate between cash and card
For the card payments I do a second level grouping for each card number
Rest flows nicely through the templates
If you have done Muenchian Grouping before, I guess this should be clear
ASKER
thank you for your help.
welcome
Do you need to group on the form of payment?
Meaning there would be other forms of payment expressed in the element name, for example
Open in new window