My current requirement is to group K18 based on ALF,OND, then K18 corresponding P04Y,P04Z,P04,P03 must be grouped in sequence as it is predefined structure of K18, but all are optional segments including K18. If any segment not exist then we can ignore that.
XSLT i used is placing the grouped values after last segment, how can i group them in sequence mentioned. if i use sort function then its giving me P03,P04,P04Y and P04Z.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<LEAV ONE="1">
<EDI SEGMENT="1">
<FIELD1>EDI</FIELD1>
</EDI>
<AK1 SEGMENT="1">
<VW>RS</VW>
</AK1>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>ACF</OND>
<P04Y SEGMENT="1">
<K_1>VALUE1</K_1>
</P04Y>
<P04Z SEGMENT="1">
<REGN>HU</REGN>
</P04Z>
<P04 SEGMENT="1">
<B_DOC>810</B_DOC>
</P04>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>10</SEQ>
<RNTAM>VALUE1</RNTAM>
</P03>
</K18>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>ACF</OND>
<P04Y SEGMENT="1">
<K_1>VALUE1</K_1>
</P04Y>
<P04Z SEGMENT="1">
<REGN>HU</REGN>
</P04Z>
<P04 SEGMENT="1">
<B_DOC>810</B_DOC>
</P04>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>20</SEQ>
<RNTAM>VALUE2</RNTAM>
</P03>
</K18>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>ACF</OND>
<P04Y SEGMENT="1">
<K_1>new value</K_1>
</P04Y>
<P04Z SEGMENT="1">
<REGN>new</REGN>
</P04Z>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>30</SEQ>
<RNTAM>VALUE2</RNTAM>
</P03>
</K18>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>FCB</OND>
</K18>
<K28 SEGMENT="1">
<BCOUN>ES</BCOUN>
</K28>
<P01 SEGMENT="1">
<FIELD1>10</FIELD1>
<FIELD2>1</FIELD2>
</P01>
<P01 SEGMENT="1">
<FIELD1>10</FIELD1>
<FIELD2>1</FIELD2>
<P02 SEGMENT="1">
<FIELD1>001</FIELD1>
</P02>
</P01>
<PS01 SEGMENT="1">
<FIELD1>VALUE</FIELD1>
</PS01>
</LEAV>
</ROOT>
** Desired Output:**
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<LEAV ONE="1">
<EDI SEGMENT="1">
<FIELD1>EDI</FIELD1>
</EDI>
<AK1 SEGMENT="1">
<VW>RS</VW>
</AK1>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>ACF</OND>
<P04Y SEGMENT="1">
<K_1>VALUE1</K_1>
</P04Y>
<P04Y SEGMENT="1">
<K_1>new value</K_1>
</P04Y>
<P04Z SEGMENT="1">
<REGN>HU</REGN>
</P04Z>
<P04Z SEGMENT="1">
<REGN>new</REGN>
</P04Z>
<P04 SEGMENT="1">
<B_DOC>810</B_DOC>
</P04>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>10</SEQ>
<RNTAM>VALUE1</RNTAM>
</P03>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>20</SEQ>
<RNTAM>VALUE2</RNTAM>
</P03>
<P03 SEGMENT="1">
<FIELD1>X</FIELD1>
<SEQ>30</SEQ>
<RNTAM>VALUE2</RNTAM>
</P03>
</K18>
<K18 SEGMENT="1">
<ALF>001</ALF>
<OND>FCB</OND>
</K18>
<K28 SEGMENT="1">
<BCOUN>ES</BCOUN>
</K28>
<P01 SEGMENT="1">
<FIELD1>10</FIELD1>
<FIELD2>1</FIELD2>
<P02 SEGMENT="1">
<FIELD1>001</FIELD1>
</P02>
<TP1 SEGMENT="1">
<ID>01</ID>
<RAS>X</RAS>
<PT2 SEGMENT="1">
<LINE>VALUE1</LINE>
</PT2>
<PT2 SEGMENT="1">
<LINE>VALUE2</LINE>
</PT2>
</TP1>
</P01>
<P01 SEGMENT="1">
<FIELD1>10</FIELD1>
<FIELD2>1</FIELD2>
<P02 SEGMENT="1">
<FIELD1>001</FIELD1>
</P02>
</P01>
<P01 SEGMENT="1">
<FIELD1>10</FIELD1>
<FIELD2>1</FIELD2>
<P02 SEGMENT="1">
<FIELD1>001</FIELD1>
</P02>
<TP1 SEGMENT="1">
<ID>01</ID>
<RAS>X</RAS>
<PT2 SEGMENT="1">
<LINE>VALUE1</LINE>
</PT2>
</TP1>
<TP1 SEGMENT="1">
<ID>02</ID>
<RAS>X</RAS>
<PT2 SEGMENT="1">
<LINE>VALUE2</LINE>
</PT2>
</TP1>
</P01>
<PS01 SEGMENT="1">
<FIELD1>VALUE</FIELD1>
</PS01>
</LEAV>
</ROOT>
** XSLT I used is below:**
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" expand-text="yes">
<xsl:template match="LEAV">
<xsl:copy>
<xsl:apply-templates select="@*, if (K18) then (* except (K18/(., following-sibling::*))) else *"/>
<xsl:for-each-group select="K18" composite="yes" group-by="ALF, OND">
<xsl:copy>
<xsl:apply-templates select="@*, ALF, OND"/>
<xsl:for-each-group select="current-group()!* except (current-group()!(ALF, OND))" composite="yes" group-by="node-name(), @*, *">
<xsl:sequence select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:for-each-group>
<xsl:apply-templates select="K18/following-sibling::*[not(self::K18)]"/>
<xsl:apply-templates select="if (K18) then (* except (K18/(., preceding-sibling::*))) else ()"/>
</xsl:copy>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
Use an xsl:sort
based on the original position stored in an accumulator:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" expand-text="yes">
<xsl:accumulator name="K18-child-pos" as="xs:integer?" initial-value="()">
<xsl:accumulator-rule match="K18" select="0"/>
<xsl:accumulator-rule match="K18/*" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="LEAV">
<xsl:copy>
<xsl:apply-templates select="@*, if (K18) then (* except (K18/(., following-sibling::*))) else *"/>
<xsl:for-each-group select="K18" composite="yes" group-by="ALF, OND">
<xsl:copy>
<xsl:apply-templates select="@*, ALF, OND"/>
<xsl:for-each-group select="current-group()!* except (current-group()!(ALF, OND))" composite="yes" group-by="node-name(), @*, *">
<xsl:sort select="accumulator-before('K18-child-pos')"/>
<xsl:sequence select="."/>
</xsl:for-each-group>
</xsl:copy>
</xsl:for-each-group>
<xsl:apply-templates select="K18/following-sibling::*[not(self::K18)]"/>
<xsl:apply-templates select="if (K18) then (* except (K18/(., preceding-sibling::*))) else ()"/>
</xsl:copy>
</xsl:template>
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy" use-accumulators="K18-child-pos"/>
</xsl:stylesheet>