Need to merge 2 xml in xslt based on a common key - xslt

I have 2 source variables which need to be merged based on a common key in XSLT
Variable 1:I have added few properties for each employee.
<EmpDetails>
<Emp>
<ID>1</ID>
<Name>A</Name>
<Address>abc 123</Address>
<Contact>1234567890</Contact>
<DOB>01/01/1989</DOB>
<Emp>
<Emp>
<ID>2</ID>
<Name>B</Name>
<Address>ASDF</Address>
<Contact>123456</Contact>
<DOB>02/02/1990</DOB>
<Emp>
</EmpDetails>
Variable 2:
<EmpAgeDetails>
<EmpAge>
<ID>1</ID>
<Age>27</Age>
<EmpAge>
<EmpAge>
<ID>2</ID>
<Age>26</Age>
<EmpAge>
</EmpAgeDetails>
Expected output:
<EmpDetails>
<Emp>
<ID>1</ID>
<Name>A</Name>
<Address>abc 123</Address>
<Contact>1234567890</Contact>
<DOB>01/01/1989</DOB>
<Age>27</Age>
<Emp>
<Emp>
<ID>2</ID>
<Name>B</Name>
<Address>ASDF</Address>
<Contact>123456</Contact>
<DOB>02/02/1990</DOB>
<Age>26</Age>
<Emp>
</EmpDetails>
I am using a template to copy all elements from Variable 1 which is working fine.
But now I need to merge that extra element of Age.
Any help is appreciated

Using XSLT 3.0 and xsl:merge, as supported in the latest Altova XMLSpy/Raptor or Saxon 9.7 EE, you can use
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:math="http://www.w3.org/2005/xpath-functions/math" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:map="http://www.w3.org/2005/xpath-functions/map" exclude-result-prefixes="array fn map math xs">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="doc1">
<EmpDetails>
<Emp>
<ID>1</ID>
<Name>A</Name>
<Address>abc 123</Address>
<Contact>1234567890</Contact>
<DOB>01/01/1989</DOB>
</Emp>
<Emp>
<ID>2</ID>
<Name>B</Name>
<Address>ASDF</Address>
<Contact>123456</Contact>
<DOB>02/02/1990</DOB>
</Emp>
</EmpDetails>
</xsl:param>
<xsl:param name="doc2">
<EmpAgeDetails>
<EmpAge>
<ID>1</ID>
<Age>27</Age>
</EmpAge>
<EmpAge>
<ID>2</ID>
<Age>26</Age>
</EmpAge>
</EmpAgeDetails>
</xsl:param>
<xsl:template match="/" name="xsl:initial-template">
<EmpDetails>
<xsl:merge>
<xsl:merge-source select="$doc1/EmpDetails/Emp">
<xsl:merge-key select="ID"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-source select="$doc2/EmpAgeDetails/EmpAge">
<xsl:merge-key select="ID"></xsl:merge-key>
</xsl:merge-source>
<xsl:merge-action>
<xsl:copy>
<xsl:apply-templates select="current-merge-group()[1]/*, fn:current-merge-group()[2]/Age"/>
</xsl:copy>
</xsl:merge-action>
</xsl:merge>
</EmpDetails>
</xsl:template>
</xsl:stylesheet>
Using XSLT 2.0 you could group:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="doc1">
<EmpDetails>
<Emp>
<ID>1</ID>
<Name>A</Name>
<Address>abc 123</Address>
<Contact>1234567890</Contact>
<DOB>01/01/1989</DOB>
</Emp>
<Emp>
<ID>2</ID>
<Name>B</Name>
<Address>ASDF</Address>
<Contact>123456</Contact>
<DOB>02/02/1990</DOB>
</Emp>
</EmpDetails>
</xsl:param>
<xsl:param name="doc2">
<EmpAgeDetails>
<EmpAge>
<ID>1</ID>
<Age>27</Age>
</EmpAge>
<EmpAge>
<ID>2</ID>
<Age>26</Age>
</EmpAge>
</EmpAgeDetails>
</xsl:param>
<xsl:template match="/" name="main">
<EmpDetails>
<xsl:for-each-group select="$doc1/EmpDetails/Emp, $doc2/EmpAgeDetails/EmpAge" group-by="ID">
<xsl:copy>
<xsl:copy-of select="current-group()[1]/*, current-group()[2]/Age"/>
</xsl:copy>
</xsl:for-each-group>
</EmpDetails>
</xsl:template>
</xsl:stylesheet>

Related

Multiply 2 filtered numbers and sum

I need to sum the multiplication of 2 numbers based on this example
<test>
<stop>
<id>1</id>
<unit_id>1</unit_id>
<unit_id>2</unit_id>
</stop>
<stop>
<id>2</id>
<unit_id>1</unit_id>
<unit_id>3</unit_id>
</stop>
<unit>
<id>1</id>
<count>2</count>
<value>1</value>
</unit>
<unit>
<id>2</id>
<count>4</count>
<value>1</value>
</unit>
<unit>
<id>3</id>
<count>2</count>
<value>3</value>
</unit>
The result i want to get is the one below
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>10</sum>
</stop>
Any tips how to get it?
I tried with this example but the sum of the moltiplication doesn't work, it is ok for only the sum or the multiplication but not both
<xsl:template match="stop">
<xsl:variable name="ship_unit" select="id"/>
<xsl:value-of select="sum(following-sibling::unit[id=$ship_unit]/count*following-sibling::unit[id=$ship_unit]/value)"/>
If I am guessing correctly, you want to do something like:
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="unit" match="unit" use="id" />
<xsl:template match="/test">
<xsl:copy>
<xsl:for-each select="stop">
<xsl:variable name="unit1" select="key('unit', unit_id[1])" />
<xsl:variable name="unit2" select="key('unit', unit_id[2])" />
<xsl:copy>
<xsl:copy-of select="id"/>
<sum>
<xsl:value-of select="$unit1/count * $unit1/value + $unit2/count * $unit2/value" />
</sum>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, the result of applying this to your input example will be:
<?xml version="1.0" encoding="utf-8"?>
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>8</sum>
</stop>
</test>
and not what you posted.

how to modify one xml input using variable from second xml input in XSLT?

My question is how to change values in <color> in sample1.xml based on the same <id> in sample2.xml
sample1.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>red</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>green></color>
</item>
</root>
sample 2.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>3</id>
<color>white</color>
</item>
</root>
expected output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>white></color>
</item>
</root>
I only know how to copy entire sample1.xml to output, but I don't know how to remember ids from sample2.xml, and by that values make changes to sample1.
Don't know if is possible, but probably I must use variables on some unknown way.
Here is my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml" />
<xsl:param name="sample1"/>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2"/>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/> <!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I really don't have idea what is the right way to do that, because I am new to XSLT 2.0. Any help will be much appreciated.
I was beaten to this by Martin Honnen, but here's my solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml"/>
<xsl:param name="sample1"/>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2"/>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/>
<!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/root/item/color">
<xsl:param name="id" select="parent::item/id/text()"/>
<xsl:copy>
<xsl:choose>
<xsl:when test="$sample2_xml/root/item[id=$id]/color">
<xsl:value-of select="$sample2_xml/root/item[id=$id]/color/text()"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="text()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
Define a key <xsl:key name="id" match="item" use="id"/> and then add a template
<xsl:template match="item[key('id', id, $sample2_xml)]/color">
<xsl:copy-of select="key('id', ../id, $sample2_xml)/color"/>
</xsl:template>
So the complete sample is
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs saxon"
xmlns:saxon="http://saxon.sf.net/"
version="2.0">
<xsl:output method="xml" indent="yes" media-type="text/xml" />
<xsl:param name="sample1" as="xs:string"><![CDATA[<root>
<item>
<id>1</id>
<color>red</color>
</item>
<item>
<id>2</id>
<color>blue</color>
</item>
<item>
<id>3</id>
<color>green></color>
</item>
</root>]]></xsl:param>
<xsl:param name="sample1_xml" select="saxon:parse($sample1)"/>
<xsl:param name="sample2" as="xs:string"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<color>yellow</color>
</item>
<item>
<id>3</id>
<color>white</color>
</item>
</root>]]></xsl:param>
<xsl:param name="sample2_xml" select="saxon:parse($sample2)"/>
<xsl:key name="id" match="item" use="id"/>
<xsl:template match="/" name="initial">
<xsl:apply-templates select="$sample1_xml/node()"/> <!-- this is only for copying entire sample1 file -->
</xsl:template>
<!-- copy all nodes and values -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[key('id', id, $sample2_xml)]/color">
<xsl:copy-of select="key('id', ../id, $sample2_xml)/color"/>
</xsl:template>
</xsl:stylesheet>

XSLT: iteration on string with indexes

I am having a trouble with very easy XSLT transformation. Let's assume that this is the input XML document:
<root>
<myString>ABBCD</myString>
</root>
The output XML should be:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
Is there an easy way to split such string and increment index over it?
It's pretty easy in XSLT 2.0...
XML Input
<root>
<myString>ABBCD</myString>
</root>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="myString">
<myCharacters>
<xsl:analyze-string select="." regex=".">
<xsl:matching-substring>
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="."/></value>
</character>
</xsl:matching-substring>
</xsl:analyze-string>
</myCharacters>
</xsl:template>
</xsl:stylesheet>
XML Output
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
It's not terrible in 1.0 either. You can use a recursive template. The following will produce the same output:
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="myString">
<myCharacters>
<xsl:call-template name="analyzeString">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</myCharacters>
</xsl:template>
<xsl:template name="analyzeString">
<xsl:param name="pos" select="1"/>
<xsl:param name="string"/>
<character>
<id><xsl:value-of select="$pos"/></id>
<value><xsl:value-of select="substring($string,1,1)"/></value>
</character>
<xsl:if test="string-length($string)>=2">
<xsl:call-template name="analyzeString">
<xsl:with-param name="pos" select="$pos+1"/>
<xsl:with-param name="string" select="substring($string,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I. XSLT 1.0 Solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vStyle" select="document('')"/>
<xsl:template match="myString">
<xsl:variable name="vStr" select="."/>
<root>
<myCharacters>
<xsl:for-each select=
"($vStyle//node()|$vStyle//#*|$vStyle//namespace::*)
[not(position() > string-length($vStr))]">
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="substring($vStr,position(),1)"/></value>
</character>
</xsl:for-each>
</myCharacters>
</root>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root>
<myString>ABBCD</myString>
</root>
the wanted, correct result is produced:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
Alternatively, with FXSL one can use the str-map template like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap" xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="xsl f ext testmap">
<xsl:import href="str-dvc-map.xsl"/>
<testmap:testmap/>
<xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="myString">
<xsl:variable name="vrtfChars">
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pStr" select="."/>
</xsl:call-template>
</xsl:variable>
<myCharacters>
<xsl:apply-templates select="ext:node-set($vrtfChars)/*"/>
</myCharacters>
</xsl:template>
<xsl:template name="enumChars" match="*[namespace-uri() = 'testmap']"
mode="f:FXSL">
<xsl:param name="arg1"/>
<character>
<value><xsl:value-of select="$arg1"/></value>
</character>
</xsl:template>
<xsl:template match="character">
<character>
<id><xsl:value-of select="position()"/></id>
<xsl:copy-of select="*"/>
</character>
</xsl:template>
</xsl:stylesheet>
to produce the same correct result:
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
II. XSLT 2.0 solution -- shorter and simpler than other answers:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="myString">
<root>
<myCharacters>
<xsl:for-each select="string-to-codepoints(.)">
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="codepoints-to-string(.)"/></value>
</character>
</xsl:for-each>
</myCharacters>
</root>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document (above), produces the wanted, correct result:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>

xslt grouping by calling template

I am new to xslt and trying to learn how to learn grouping using keys and using templates.
Can somebody help me on how can do the following in xslt.
I have to call a template from another template to do the transformation.
Thanks
here is my xml.
<Doc>
<ExOrder>121</ExOrder>
<Line>
<Ordernumber>225</Ordernumber>
<OrderID>1</OrderID>
<Quantity>5</Quantity>
</Line>
<Line>
<Ordernumber>225</Ordernumber>
<OrderID>5</OrderID>
<Quantity>5</Quantity>
</Line>
<Line>
<Ordernumber>226</Ordernumber>
<OrderID>2</OrderID>
<Quantity>5</Quantity>
</Line>
And here is how it should look like after.
<Doc>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
</Doc>
I'm going to assume the output you actually want is:
<Doc>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
<Order>
<Ordernumber>226</Ordernumber>
<Line>
<ID>2</ID>
</Line>
</Order>
</Doc>
Since the sample output you provided makes no sense. This XSLT will produce the output above when run on your sample input:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kOrder" match="Line" use="Ordernumber"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<Doc>
<xsl:apply-templates select="Line[generate-id() =
generate-id(key('kOrder', Ordernumber)[1])]" />
</Doc>
</xsl:template>
<xsl:template match="Line">
<Order>
<xsl:apply-templates select="Ordernumber" />
<Line>
<xsl:apply-templates select="key('kOrder', Ordernumber)/OrderID" />
</Line>
</Order>
</xsl:template>
<xsl:template match="OrderID">
<ID>
<xsl:value-of select="."/>
</ID>
</xsl:template>
</xsl:stylesheet>

XSLT transform to multiple complex types within for loop

I have a requirment to transform a XML with the below structure
<CustomerStatements>
<CustomerStatement>
<Name>ABC</Name>
<ID>1</ID>
<Amt>10</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>ABC</Name>
<ID>1</ID>
<Amt>20</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>XYZ</Name>
<ID>2</ID>
<Amt>30</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>XYZ</Name>
<ID>2</ID>
<Amt>40</Amt>
</CustomerStatement>
</CustomerStatements>
To
<Customers>
<Customer>
<Name>ABC</Name>
<Id>1</Id>
<Amounts>
<Amount>10</Amount>
<Amount>20</Amount>
</Amounts>
</Customer>
<Customer>
<Name>XYZ</Name>
<Id>2</Id>
<Amount>30</Amount>
<Amount>40</Amount>
</Customer>
</Customers>
I tried using a for loop and taking the name into a variable to compare the name in the next record, but this doesn't work. Can you any one help me with a sample XSLT psudo code.
Thanks
I. When this XSLT 1.0 solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key
name="kCustByNameId"
match="CustomerStatement"
use="concat(Name, '+', ID)" />
<xsl:template match="/*">
<Customers>
<xsl:apply-templates
select="CustomerStatement[
generate-id() =
generate-id(key('kCustByNameId', concat(Name, '+', ID))[1])]" />
</Customers>
</xsl:template>
<xsl:template match="CustomerStatement">
<Customer>
<xsl:copy-of select="Name|ID" />
<Amounts>
<xsl:for-each select="key('kCustByNameId', concat(Name, '+', ID))/Amt">
<Amount>
<xsl:apply-templates />
</Amount>
</xsl:for-each>
</Amounts>
</Customer>
</xsl:template>
</xsl:stylesheet>
...is applied to the OP's original XML:
<CustomerStatements>
<CustomerStatement>
<Name>ABC</Name>
<ID>1</ID>
<Amt>10</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>ABC</Name>
<ID>1</ID>
<Amt>20</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>XYZ</Name>
<ID>2</ID>
<Amt>30</Amt>
</CustomerStatement>
<CustomerStatement>
<Name>XYZ</Name>
<ID>2</ID>
<Amt>40</Amt>
</CustomerStatement>
</CustomerStatements>
...the wanted result is produced:
<?xml version="1.0" encoding="UTF-8"?><Customers>
<Customer>
<Name>ABC</Name>
<ID>1</ID>
<Amounts>
<Amount>10</Amount>
<Amount>20</Amount>
</Amounts>
</Customer>
<Customer>
<Name>XYZ</Name>
<ID>2</ID>
<Amounts>
<Amount>30</Amount>
<Amount>40</Amount>
</Amounts>
</Customer>
</Customers>
The primary thing to look at here is Muenchian Grouping, which is the generally accepted method for grouping problems in XSLT 1.0.
II. Here's a more compact XSLT 2.0 solution:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<Customers>
<xsl:for-each-group select="CustomerStatement" group-by="ID">
<Customer>
<xsl:copy-of select="current-group()[1]/Name|current-group()[1]/ID" />
<Amounts>
<xsl:for-each select="current-group()/Amt">
<Amount>
<xsl:apply-templates />
</Amount>
</xsl:for-each>
</Amounts>
</Customer>
</xsl:for-each-group>
</Customers>
</xsl:template>
</xsl:stylesheet>
In this case, notice XSLT 2.0's use of the for-each-group element, which eliminates the need for the sometimes-verbose and potentially confusing Muenchian Grouping method.