Reputation: 75
I have a transformation where I am trying to record a set of flattened transaction details and at the end of the file provide a summation of totals grouped by tender type and register number.
Recording the set of individual transaction details is the easy part and I have that working fine, but I am struggling with the summary portion.
The trouble is that I won’t know what or how many different register numbers there are, or what or how many tender types there are (so explicitly listing out summaries in the xslt with static filter strings is a no-go), so some sort of grouping seems to be in order.
One more wrench – I’m stuck using XSLT 1.0…
I tried messing around with the muenchian grouping, but between the compound key requirement (with register living at a different level than payment method) and my limited understanding on how the muenchian method and keys work in the first place I couldn’t seem to get it working, but I think it still may be the trick needed…
Any suggestions on how I might muenchia-magically get this to work?
here's an example source document:
<s0:SalesCollection xmlns:s0="http://mySourceSchema">
<s0:Sale transactionnumber="1" register="1">
<s0:Tender amount="1.11" paymentmethod="visa" />
<s0:Tender amount="2.22" paymentmethod="mastercard" />
</s0:Sale>
<s0:Sale transactionnumber="2" register="1">
<s0:Tender amount="5.55" paymentmethod="discover" />
<s0:Tender amount="4.44" paymentmethod="visa" />
</s0:Sale>
<s0:Sale transactionnumber="1" register="2">
<s0:Tender amount="9.99" paymentmethod="amex" />
<s0:Tender amount="8.88" paymentmethod="visa" />
</s0:Sale>
</s0:SalesCollection>
here's what I'm going for (again, I have the record[@type='detail'] records working already):
<ns0:root xmlns:ns0="http://myDestinationSchema">
<ns0:record type="detail" transactionnumber="1" register="1" amount="1.11" paymentmethod="visa" />
<ns0:record type="detail" transactionnumber="1" register="1" amount="2.22" paymentmethod="mastercard" />
<ns0:record type="detail" transactionnumber="2" register="1" amount="5.55" paymentmethod="discover" />
<ns0:record type="detail" transactionnumber="2" register="1" amount="4.44" paymentmethod="visa" />
<ns0:record type="detail" transactionnumber="1" register="2" amount="9.99" paymentmethod="amex" />
<ns0:record type="detail" transactionnumber="1" register="2" amount="8.88" paymentmethod="visa" />
<ns0:record type="summary" register="1" amount="5.55" paymentmethod="visa" />
<ns0:record type="summary" register="1" amount="2.22" paymentmethod="mastercard" />
<ns0:record type="summary" register="1" amount="5.55" paymentmethod="discover" />
<ns0:record type="summary" register="2" amount="9.99" paymentmethod="amex" />
<ns0:record type="summary" register="2" amount="8.88" paymentmethod="visa" />
</ns0:root>
So how do I go about creating the summary records grouped by register and paymentmethod using xslt 1.0?
Upvotes: 2
Views: 1442
Reputation: 243569
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:s0="http://mySourceSchema"
xmlns:ns0="http://myDestinationSchema" exclude-result-prefixes="s0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kTendByTypeAndReg" match="s0:Tender"
use="concat(../@register, '#', @paymentmethod)"/>
<xsl:template match="/*">
<ns0:root xmlns:ns0="http://myDestinationSchema">
<xsl:apply-templates select="*/*"/>
<xsl:apply-templates mode="group" select=
"*/*[generate-id()
=generate-id(key('kTendByTypeAndReg',
concat(../@register, '#', @paymentmethod))[1]
)
]"/>
</ns0:root>
</xsl:template>
<xsl:template match="s0:Tender">
<ns0:record type="detail" transactionnumber="{../@transactionnumber}"
register="{../@register}" amount="{@amount}" paymentmethod="{@paymentmethod}" />
</xsl:template>
<xsl:template match="*" mode="group">
<ns0:record type="summary" register="{../@register}" paymentmethod="{@paymentmethod}"
amount="{sum(key('kTendByTypeAndReg',concat(../@register,'#',@paymentmethod))
/@amount)}"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<s0:SalesCollection xmlns:s0="http://mySourceSchema">
<s0:Sale transactionnumber="1" register="1">
<s0:Tender amount="1.11" paymentmethod="visa" />
<s0:Tender amount="2.22" paymentmethod="mastercard" />
</s0:Sale>
<s0:Sale transactionnumber="2" register="1">
<s0:Tender amount="5.55" paymentmethod="discover" />
<s0:Tender amount="4.44" paymentmethod="visa" />
</s0:Sale>
<s0:Sale transactionnumber="1" register="2">
<s0:Tender amount="9.99" paymentmethod="amex" />
<s0:Tender amount="8.88" paymentmethod="visa" />
</s0:Sale>
</s0:SalesCollection>
produces the wanted result:
<ns0:root xmlns:ns0="http://myDestinationSchema">
<ns0:record type="detail" transactionnumber="1" register="1" amount="1.11" paymentmethod="visa"/>
<ns0:record type="detail" transactionnumber="1" register="1" amount="2.22" paymentmethod="mastercard"/>
<ns0:record type="detail" transactionnumber="2" register="1" amount="5.55" paymentmethod="discover"/>
<ns0:record type="detail" transactionnumber="2" register="1" amount="4.44" paymentmethod="visa"/>
<ns0:record type="detail" transactionnumber="1" register="2" amount="9.99" paymentmethod="amex"/>
<ns0:record type="detail" transactionnumber="1" register="2" amount="8.88" paymentmethod="visa"/>
<ns0:record type="summary" register="1" paymentmethod="visa" amount="5.55"/>
<ns0:record type="summary" register="1" paymentmethod="mastercard" amount="2.22"/>
<ns0:record type="summary" register="1" paymentmethod="discover" amount="5.55"/>
<ns0:record type="summary" register="2" paymentmethod="amex" amount="9.99"/>
<ns0:record type="summary" register="2" paymentmethod="visa" amount="8.88"/>
</ns0:root>
Explanation:
Proper use of:
AVT s (Attribute Value Templates).
Upvotes: 3
Reputation: 3738
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://myDestinationSchema"
xmlns:s0="http://mySourceSchema"
exclude-result-prefixes="s0"
version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key
name="kTenderByRegisterAndMethod"
match="s0:Tender"
use="concat(parent::*/@register, '+', @paymentmethod)"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<ns0:root>
<xsl:apply-templates select="*/s0:Tender"/>
<xsl:apply-templates
select="*/s0:Tender[generate-id() =
generate-id(key(
'kTenderByRegisterAndMethod',
concat(parent::*/@register,
'+',
@paymentmethod))[1])]"
mode="summary"/>
</ns0:root>
</xsl:template>
<xsl:template match="s0:Tender">
<ns0:record type="detail">
<xsl:apply-templates select="parent::*/@*|@*"/>
</ns0:record>
</xsl:template>
<xsl:template match="s0:Tender" mode="summary">
<ns0:record type="summary" register="{parent::*/@register}">
<xsl:attribute name="amount">
<xsl:value-of
select="sum(
key('kTenderByRegisterAndMethod',
concat(parent::*/@register,
'+', @paymentmethod))/@amount)"/>
</xsl:attribute>
<xsl:apply-templates select="@*[not(name() = 'amount')]"/>
</ns0:record>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML:
<s0:SalesCollection xmlns:s0="http://mySourceSchema">
<s0:Sale transactionnumber="1" register="1">
<s0:Tender amount="1.11" paymentmethod="visa"/>
<s0:Tender amount="2.22" paymentmethod="mastercard"/>
</s0:Sale>
<s0:Sale transactionnumber="2" register="1">
<s0:Tender amount="5.55" paymentmethod="discover"/>
<s0:Tender amount="4.44" paymentmethod="visa"/>
</s0:Sale>
<s0:Sale transactionnumber="1" register="2">
<s0:Tender amount="9.99" paymentmethod="amex"/>
<s0:Tender amount="8.88" paymentmethod="visa"/>
</s0:Sale>
</s0:SalesCollection>
...the wanted result is produced:
<ns0:root xmlns:ns0="http://myDestinationSchema">
<ns0:record type="detail" transactionnumber="1" register="1" amount="1.11" paymentmethod="visa"/>
<ns0:record type="detail" transactionnumber="1" register="1" amount="2.22" paymentmethod="mastercard"/>
<ns0:record type="detail" transactionnumber="2" register="1" amount="5.55" paymentmethod="discover"/>
<ns0:record type="detail" transactionnumber="2" register="1" amount="4.44" paymentmethod="visa"/>
<ns0:record type="detail" transactionnumber="1" register="2" amount="9.99" paymentmethod="amex"/>
<ns0:record type="detail" transactionnumber="1" register="2" amount="8.88" paymentmethod="visa"/>
<ns0:record type="summary" register="1" amount="5.55" paymentmethod="visa"/>
<ns0:record type="summary" register="1" amount="2.22" paymentmethod="mastercard"/>
<ns0:record type="summary" register="1" amount="5.55" paymentmethod="discover"/>
<ns0:record type="summary" register="2" amount="9.99" paymentmethod="amex"/>
<ns0:record type="summary" register="2" amount="8.88" paymentmethod="visa"/>
</ns0:root>
Explanation
You were on the right track to examine Muenchian Grouping. Note the composite key I used that matches s0:Tender
elements by concatenating their parent's @register
value, a "+" sign (present for convenience and to ensure that the concatenation is never mangled), and their @paymentmethod
value.
Stick with Muenchian Grouping, even if it's not the easiest concept to understand right off the bat. I, myself, learned by looking at Muenchian Grouping-related questions on SO and attempting to answer them; over time, it began to clear up for me.
Upvotes: 1