Keep main element after sorting with xsl - xslt

I have a source xml:
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000046741</TKNUM>
<E1EDL20 SEGMENT="1">
<VBELN>1</VBELN>
<VKORG>0200</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>1</VBELN>
<VKORG>0100</VKORG>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
</ns0:Message1>
</ns0:Messages>
I'm sorting all E1EDL20 elements with xslt:
<xsl:template match="E1EDT20">
<xsl:for-each select="*[not(self::E1EDL20)]">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:for-each select="E1EDL20">
<xsl:sort select="VKORG"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
<!-- Default Template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
But in my result I'm missing the parent E1EDT20 element:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Messages xmlns:ns0="http://sap.com/xi/XI/SplitAndMerge">
<ns0:Message1>
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<TKNUM>0000046741</TKNUM>
<E1EDL20 SEGMENT="1">
<VBELN>1</VBELN>
<VKORG>0100</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>1</VBELN>
<VKORG>0200</VKORG>
</E1EDL20>
</IDOC>
</SHPMNT05>
</ns0:Message1>
</ns0:Messages>
What do I need to put after
<xsl:template match="E1EDT20">
to keep the element / tag in the output?
Very sorry to bother you with this beginners question but every so often I have to use xslt and I can't find the answer after search for quite some time...
Mike

In your template which matches E1EDT20 you don't copy the element; you only copy its children.
You should copy the element with xsl:copy, and within that, copy its attributes, and then process the child elements:
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="*[not(self::E1EDL20)]">
<xsl:copy-of select="."/>
</xsl:for-each>
<xsl:for-each select="E1EDL20">
<xsl:sort select="VKORG"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>

Wrap the content of your template in an xsl:copy instruction. You can also shorten it to:
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:copy-of select="#* | *[not(self::E1EDL20)]"/>
<xsl:for-each select="E1EDL20">
<xsl:sort select="VKORG"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:copy>
</xsl:template>

Related

XSL to suppress xml declaration conditonally (fails)

I fail to suppress the xml declaration with my following xsl. I want a completely empty xml when OWNER=0000080070:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:choose>
<xsl:when test="//IDOC/ZSCWM_TU[OWNER='0000080070']">
</xsl:when>
<xsl:otherwise>
<xsl:text disable-output-escaping="yes"><?xml version="1.0" encoding="UTF-8"?>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates/>
</xsl:template>
<!-- Special template -->
<xsl:template match="/*[//IDOC/ZSCWM_TU[OWNER='0000080070']]">
</xsl:template>
<!-- Default Template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Input xml:
<?xml version="1.0" encoding="UTF-8"?>
<ZSCWM_-TU_LOADLIST>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<ZSCWM_TU SEGMENT="1">
<OWNER>0000080070</OWNER>
</ZSCWM_TU>
</IDOC>
</ZSCWM_-TU_LOADLIST>
Current output:
<?xml version="1.0" encoding="UTF-8"?>
Required output:
BLANK XML
I must be making a stupid mistake :(
Kind regards,
Mike
If you're using an XSLT 2.0 processor such as Saxon then I would recommend
<xsl:template match="/">
<xsl:result-document omit-xml-declaration=
"{if (//IDOC/ZSCWM_TU[OWNER='0000080070']) then 'yes' else 'no'}">
<xsl:apply-templates/>
</xsl:result-document>
</xsl:template>

XSLT Sort nested xml

I need to sort the below XML from SAP based on E1EDP01 - POSEX and ACTION, rest of the xml should be copied as it is. Please note that there's E1EDP19 under E1EDP01, which should be copied as it is as well. My XSLT is not working bcoz am not sure what should be the value for the template match. Please help.
<ORDERS05>
<IDOC BEGIN="1">
<E1EDK14 SEGMENT="1">
<QUALF>014</QUALF>
<ORGID>PF01</ORGID>
</E1EDK14>
<E1EDP01 SEGMENT="1">
<POSEX>00020</POSEX>
<ACTION>001</ACTION>
<PSTYP>0</PSTYP>
<MENGE>540.000</MENGE>
<MENEE>RO</MENEE>
<E1EDP19>
<Text> Test 2 </Text>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<POSEX>00030</POSEX>
<ACTION>001</ACTION>
<PSTYP>1</PSTYP>
<MENGE>140.000</MENGE>
<MENEE>AD</MENEE>
<E1EDP19>
<Text> Test 1 </Text>
</E1EDP19>
</E1EDP01>
<E1EDS01 SEGMENT="1">
<SUMID>002</SUMID>
<SUMME>832.2</SUMME>
<SUNIT>USD</SUNIT>
</E1EDS01>
</IDOC>
</ORDERS05>
<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:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- -->
<xsl:template match="E1EDP01">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="POSEX">
<xsl:sort select="POSEX" order="descending" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Expected Result
<ORDERS05>
<IDOC BEGIN="1">
<E1EDK14 SEGMENT="1">
<QUALF>014</QUALF>
<ORGID>PF01</ORGID>
</E1EDK14>
<E1EDP01 SEGMENT="1">
<POSEX>00030</POSEX>
<ACTION>001</ACTION>
<PSTYP>1</PSTYP>
<MENGE>140.000</MENGE>
<MENEE>AD</MENEE>
<E1EDP19>
<Text> Test 1 </Text>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<POSEX>00020</POSEX>
<ACTION>001</ACTION>
<PSTYP>0</PSTYP>
<MENGE>540.000</MENGE>
<MENEE>RO</MENEE>
<E1EDP19>
<Text> Test 2 </Text>
</E1EDP19>
</E1EDP01>
<E1EDS01 SEGMENT="1">
<SUMID>002</SUMID>
<SUMME>832.2</SUMME>
<SUNIT>USD</SUNIT>
</E1EDS01>
</IDOC>
</ORDERS05>
If you want to sort the E1EDP01 elements then by using xsl:sort in an xsl:apply-templates of a template processing the children of the parent element:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[E1EDP01]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="node()" group-adjacent="boolean(self::E1EDP01)">
<xsl:choose>
<xsl:when test="current-grouping-key()">
<xsl:apply-templates select="current-group()">
<xsl:sort select="POSEX" order="descending"/>
<xsl:sort select="ACTION" order="descending"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
http://xsltransform.net/ncntCT9/1 has an online example.

How can I summarize nodes using calculation data from other nodes

Using XSLT 1.0, how can I summarize subnodes under a given node while modifiyng the content with data from another set of nodes in an elegant way? Assume I have this xml:
<Root>
<ExchangeRates>
<ExchangeRate>
<CurrencyCode>USD</CurrencyCode>
<Rate>6.4</Rate>
</ExchangeRate>
<ExchangeRate>
<CurrencyCode>EUR</CurrencyCode>
<Rate>8.44</Rate>
</ExchangeRate>
<ExchangeRate>
<CurrencyCode>SEK</CurrencyCode>
<Rate>1</Rate>
</ExchangeRate>
</ExchangeRates>
<Prices>
<Price>
<Currency>SEK</Currency>
<Amount>10000</Amount>
</Price>
<Price>
<Currency>EUR</Currency>
<Amount>1000</Amount>
</Price>
<Price>
<Currency>USD</Currency>
<Amount>1000</Amount>
</Price>
</Prices>
</Root>
I want the sum of all Amounts converted into SEK with the help of the ExchangeRates. The result should be:
<SumInSEK>24840</SumInSEK>
If I didn't have to convert the amounts I would simply use the xpath sum() function. Is is possible to use that function in this case?
Another possible solution without recursive calls but with exsl extension.
This make use of the key definition form #softwarebear.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:key name="rates" match="//ExchangeRate/Rate" use="parent::*/child::CurrencyCode/text()"/>
<xsl:template match="/" >
<xsl:apply-templates select="//Prices"/>
</xsl:template>
<xsl:template match="Prices">
<SUmInSEK>
<xsl:variable name="price_SEK">
<xsl:apply-templates mode="SEK" />
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($price_SEK)//price_SEK)"/>
</SUmInSEK>
</xsl:template>
<xsl:template match="Price" mode="SEK">
<price_SEK>
<xsl:value-of
select="number(Amount) * number( key('rates', Currency) )" />
</price_SEK>
</xsl:template>
</xsl:stylesheet>
Wiht the output
<SUmInSEK>24840</SUmInSEK>
I thought this might be simple ... but I don't think you can use sum() on this occasion ... the best I can do is a recursive template.
?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:key name="rates" match="//ExchangeRate/Rate" use="parent::*/child::CurrencyCode/text()"/>
<xsl:template match="//Prices">
<SUmInSEK>
<xsl:call-template name="sum"/>
</SUmInSEK>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="iterator" select="1"/>
<xsl:param name="total" select="0"/>
<xsl:variable name="price" select="child::Price[$iterator]"/>
<xsl:variable name="current">
<xsl:value-of select="number($price/child::Amount) * number( key('rates', $price/child::Currency) )"/>
</xsl:variable>
<xsl:variable name="newtotal">
<xsl:value-of select="$total + $current"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$price/following-sibling::Price">
<xsl:call-template name="sum">
<xsl:with-param name="iterator" select="$iterator+1"/>
<xsl:with-param name="total" select="$newtotal"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$total + $current"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="* | /">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text() | #*">
</xsl:template>
<xsl:template match="processing-instruction() | comment()" />
</xsl:stylesheet>

xml to xml conversion using xslt

I need to pass the XML to third party system which can be understand by third party and parse it.
Below is my input xml which I created data by fetching from database.
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
**<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppLimitDetailsBO>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</CorpAppLimitDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>3</ProductCategory>
</CorpAppProductDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>1</ProductCategory>
</CorpAppProductDetailsBO>
<CorpAppProductDetailsBO>
<ProductCategory>2</ProductCategory>
</CorpAppProductDetailsBO>**
<TemplateDetails>
<Template>tempid001</Template>
</TemplateDetails>
<SelectedClauses>
<Clauses>
<Clause>clause1</Clause>
</Clauses>
<Clauses>
<Clause>clause2</Clause>
</Clauses>
<Clauses>
<Clause>clause3</Clause>
</Clauses>
</SelectedClauses>
<Distribution>
<Email>email1#domain.com,email2#domain.com,email3#domain.com</Email>
<Print>blrkec3030,blrkec3031</Print>
</Distribution>
</Data>
</Body>
</FIXML>
I want to convert this input XML to another XML format using XSLT.
Below is the format which I need,
<FIXML>
<Header>
<RequestID>ReqID8942</RequestID>
<RequestType>DocGen</RequestType>
<Version>10.6</Version>
<BankId>01</BankId>
<ChannelId>LOS</ChannelId>
</Header>
<Body>
<Data>
**<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
</LimitDetails>
<ProductDetails>
<Product>
<ProductCategory>3</ProductCategory>
</Product>
<Product>
<ProductCategory>1</ProductCategory>
</Product>
<Product>
<ProductCategory>2</ProductCategory>
</Product>
</ProductDetails>**
<TemplateDetails>
<Template>tempid001</Template>
</TemplateDetails>
<SelectedClauses>
<Clauses>
<Clause>clause1</Clause>
</Clauses>
<Clauses>
<Clause>clause2</Clause>
</Clauses>
<Clauses>
<Clause>clause3</Clause>
</Clauses>
</SelectedClauses>
<Distribution>
<Email>email1#domain.com,email2#domain.com,email3#domain.com</Email>
<Print>blrkec3030,blrkec3031</Print>
</Distribution>
</Data>
</Body>
</FIXML>
Please help me out as I have to complete the task in another 2 days of time.
I tried with below code and i m getting below outputs but rest of the tags for e.g. <FIXML> , <TemplateDetails> and etc is not coming as part of output xml.
xsl code below:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes" /> <!-- This identity template copies the document -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<!--
This template will only match the 'CorpAppLimitDetailsBO'
nodes and modify them the way you want.
-->
<xsl:template match="/*">
<xsl:element name="LimitDetails">
<xsl:for-each select="//CorpAppLimitDetailsBO">
<xsl:element name="Limit">
<xsl:for-each select="*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
<xsl:element name="ProductDetails">
<xsl:for-each select="//CorpAppProductDetailsBO">
<xsl:element name="Product">
<xsl:for-each select="*">
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output.xml below:
<?xml version="1.0" encoding="UTF-8"?>
<LimitDetails>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>ROOT</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>0.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY/>
<ApprovedLimit>500.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
<Limit>
<ApprovedLimitHomeCCY>100.0</ApprovedLimitHomeCCY>
<ApprovedLimitCCY>INR</ApprovedLimitCCY>
<ApprovedLimit>100.0</ApprovedLimit>
<LimitClassification>CLASSIFICATION1</LimitClassification>
</Limit>
</LimitDetails>
<ProductDetails>
<Product>
<ProductCategory>3</ProductCategory>
</Product>
<Product>
<ProductCategory>1</ProductCategory>
</Product>
<Product>
<ProductCategory>2</ProductCategory>
</Product>
</ProductDetails>
Note: The child tag(for e.g.ApprovedLimitHomeCCY....) which i present under BO's(for e.g. CorpAppLimitDetailsBO) tag are dynamic.I shouldnt hard code in xsl.I m new to XSLT. pls help me out.
Thanks Shil and Sean for your solution. both are perfect for my requirements. but i have one more doubt now.i m adding one more child tag <DBApplicantMiscDetails> under
<CorpAppProductDetailsBO>
Input xml:
<CorpAppProductDetailsBO>
<ProductCategory>2</ProductCategory>
<DBApplicantMiscDetails>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</DBApplicantMiscDetails>
</CorpAppProductDetailsBO>
Below is the output format which i expect.
<ProductDetails>
<Product>
<ProductCategory>2</ProductCategory>
<APPLICANTMISCID>400000</APPLICANTMISCID>
<APPLICANTID>400030</APPLICANTID>
<MISCTYPE>APPLIED</MISCTYPE>
</Product>
</ProductDetails>
Thanks again.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*" />
<xsl:key name="kDetails" match="*
[starts-with(name(),'CorpApp') and
substring(name(), string-length(name()) - 8) = 'DetailsBO']"
use="substring-before(name(),'DetailsBO')" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[*[key('kDetails',substring-before(name(),'DetailsBO'))]]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="*[generate-id() =
generate-id(key('kDetails',
substring-before(name(),'DetailsBO'))[1])]" mode="group" />
<xsl:apply-templates select="*[not(
key('kDetails',substring-before(name(),'DetailsBO')))]
|comment()|processing-instruction()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*" mode="group">
<xsl:variable name="group-name" select="substring-after(substring-before(name(),'DetailsBO'),'CorpApp')" />
<xsl:element name="{$group-name}Details">
<xsl:for-each select="key('kDetails',substring-before(name(),'DetailsBO'))">
<xsl:element name="{$group-name}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="FIXML"/>
</xsl:template>
<xsl:template match="FIXML">
<FIXML>
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Body"/>
</FIXML>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Body">
<Body>
<xsl:apply-templates select="Data"/>
</Body>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates select="CorpAppLimitDetailsBO"/>
</LimitDetails>
<ProductDetails>
<xsl:apply-templates select="CorpAppProductDetailsBO"/>
</ProductDetails>
<xsl:apply-templates select="TemplateDetails"/>
<xsl:apply-templates select="SelectedClauses"/>
<xsl:apply-templates select="Distribution"/>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:copy-of select="child::*"/>
</Limit>
</xsl:template>
<xsl:template match="CorpAppProductDetailsBO">
<xsl:apply-templates select="ProductCategory"/>
</xsl:template>
<xsl:template match="ProductCategory">
<Product>
<xsl:copy-of select="."/>
</Product>
</xsl:template>
<xsl:template match="TemplateDetails">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="SelectedClauses">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Distribution">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:apply-templates select="FIXML"/>
</xsl:template>
<xsl:template match="FIXML">
<FIXML>
<xsl:apply-templates select="Header"/>
<xsl:apply-templates select="Body"/>
</FIXML>
</xsl:template>
<xsl:template match="Header">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Body">
<Body>
<xsl:apply-templates select="Data"/>
</Body>
</xsl:template>
<xsl:template match="Data">
<Data>
<LimitDetails>
<xsl:apply-templates select="CorpAppLimitDetailsBO"/>
</LimitDetails>
<ProductDetails>
<xsl:apply-templates select="CorpAppProductDetailsBO"/>
</ProductDetails>
<xsl:apply-templates select="TemplateDetails"/>
<xsl:apply-templates select="SelectedClauses"/>
<xsl:apply-templates select="Distribution"/>
</Data>
</xsl:template>
<xsl:template match="CorpAppLimitDetailsBO">
<Limit>
<xsl:copy-of select="child::*"/>
</Limit>
</xsl:template>
<xsl:template match="CorpAppProductDetailsBO">
<Product>
<xsl:apply-templates select="ProductCategory"/>
<xsl:apply-templates select="DBApplicantMiscDetails"/>
</Product>
</xsl:template>
<xsl:template match="ProductCategory">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="DBApplicantMiscDetails">
<xsl:copy-of select="child::*"/>
</xsl:template>
<xsl:template match="TemplateDetails">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="SelectedClauses">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="Distribution">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>

Joining data of the same type with XSLT

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?
There's probably a better way, but try this:
http://www.xmlplayground.com/2A3C7H
(see output source)
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.