Reputation: 1571
I would like to join all data of the same type with XSLT. I have the following XML:
<ZE1MARAM>
<ZE1KONDM SEGMENT="1">
<VKORG>NL01</VKORG>
<KONDART>VKP0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NL01</KONDWERT>
<MENGE> 70.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
<ZE1KONDM SEGMENT="1">
<VKORG>NLWS</VKORG>
<KONDART>VKP0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NLWS</KONDWERT>
<MENGE> 70.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
<ZE1KONDM SEGMENT="1">
<VKORG>NLWS</VKORG>
<KONDART>VKA0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NLWS</KONDWERT>
<MENGE> 33.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
</ZE1MARAM>
so each ZE1KONDM with the same VKORG value in the result xml has to be appended to the same element. So the result would be something like that:
<result>
<prices value="NL01">
<price type="VKP0">
70.00
</price>
</prices>
<prices value="NLWS">
<price type="VKP0">
70.00
</price>
<price type="VKA0">
55.00
</price>
</prices>
I tried to work with keys, and do something like that:
<xsl:key name="myKey" match="ZE1KONDM" use="normalize-space(VKORG)" />
<xsl:for-each select="ZE1KONDM">
<xsl:choose>
<xsl:when test="KONDART='VKP0'">
<xsl:element name="prices">
<xsl:element name="price">
<xsl:value-of select="key('myKey', normalize-space(VKORG))/MENGE"/>
</xsl:element>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:for-each>
but it does not work because it takes just one key..
There is some way to solve this problem with xslt?
Upvotes: 1
Views: 66
Reputation: 243579
I. XSLT 1.0 Solution:
Here is a classical application of the Muenchian grouping method:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kZByM" match="ZE1KONDM" use="VKORG"/>
<xsl:template match="/*">
<result>
<xsl:apply-templates select=
"*[generate-id() = generate-id(key('kZByM', VKORG)[1])]"/>
</result>
</xsl:template>
<xsl:template match="ZE1KONDM">
<prices value="{VKORG}">
<xsl:apply-templates select="key('kZByM', VKORG)" mode="inGroup"/>
</prices>
</xsl:template>
<xsl:template match="ZE1KONDM" mode="inGroup">
<price type="{KONDART}">
<xsl:value-of select="MENGE"/>
</price>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<ZE1MARAM>
<ZE1KONDM SEGMENT="1">
<VKORG>NL01</VKORG>
<KONDART>VKP0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NL01</KONDWERT>
<MENGE>70.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
<ZE1KONDM SEGMENT="1">
<VKORG>NLWS</VKORG>
<KONDART>VKP0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NLWS</KONDWERT>
<MENGE>70.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
<ZE1KONDM SEGMENT="1">
<VKORG>NLWS</VKORG>
<KONDART>VKA0</KONDART>
<BEGINDATUM>99991231</BEGINDATUM>
<ENDDATUM>20120605</ENDDATUM>
<KONDWERT>NLWS</KONDWERT>
<MENGE>33.00</MENGE>
<CURRENCY>EUR</CURRENCY>
</ZE1KONDM>
</ZE1MARAM>
the wanted, correct result is produced:
<result>
<prices value="NL01">
<price type="VKP0">70.00</price>
</prices>
<prices value="NLWS">
<price type="VKP0">70.00</price>
<price type="VKA0">33.00</price>
</prices>
</result>
Do note: The Muenchian grouping method is probably the fastest XSLT 1.0 grouping method, because it uses keys. Other methods (such as comparing siblings values) are way too slower (O(N^2)) which is prohibitive fro using them on large data sizes.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<result>
<xsl:for-each-group select="*" group-by="VKORG">
<prices value="{VKORG}">
<xsl:apply-templates select="current-group()"/>
</prices>
</xsl:for-each-group>
</result>
</xsl:template>
<xsl:template match="ZE1KONDM">
<price type="{KONDART}">
<xsl:value-of select="MENGE"/>
</price>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the same correct result is produced:
<result>
<prices value="NL01">
<price type="VKP0">70.00</price>
</prices>
<prices value="NLWS">
<price type="VKP0">70.00</price>
<price type="VKA0">33.00</price>
</prices>
</result>
Explanation:
Proper use of xsl:for-each-group
with the group-by
attribute, and the current-group()
function.
Upvotes: 1
Reputation: 34596
There's probably a better way, but try this:
http://www.xmlplayground.com/2A3C7H
(see output source)
Upvotes: 1