xslt-2.0xslt-3.0

Grouping based on key fields and handling optional segments


My current requirement is to group K18 based on ALF,OND, if it matches corresponding P04Y,P04Z,P04,P03 need to club together in order (P04Y group followed by p04z,p04,p03), all these P04Y,P04Z,P04,P03 are not mandatory segments in input.

in addition to above, need to club P01/TP1 if it has same ID and RAS then we need to club PT2 under same TP1 segment. if there is no TP1 segment then we can pass P01 segment as it is, TP1 is not mandatory segment. we can have the input with no TP1 segments

XSLT i used is deleting P01 segment if it has no TP1 segment, Please check once

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>
            <P04 SEGMENT="1">
                <B_DOC>new value</B_DOC>
            </P04>
            <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>
            </TP1>
            <TP1 SEGMENT="1">
                <ID>01</ID>
                <RAS>X</RAS>
                <PT2 SEGMENT="1">
                    <LINE>VALUE2</LINE>
                </PT2>
            </TP1>
            <TP1 SEGMENT="1">
                <ID>01</ID>
                <RAS>X</RAS>
                <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>

** 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>
            <P04 SEGMENT="1">
                <B_DOC>new value</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>

input 2

<?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>
            <P04 SEGMENT="1">
                <B_DOC>new value</B_DOC>
            </P04>
            <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>            
        </P01>
        <P01 SEGMENT="1">
            <FIELD1>20</FIELD1>
            <FIELD2>1</FIELD2>
            <P02 SEGMENT="1">
                <FIELD1>001</FIELD1>
            </P02>
        </P01>
        <P01 SEGMENT="1">
            <FIELD1>30</FIELD1>
            <FIELD2>1</FIELD2>
            <P02 SEGMENT="1">
                <FIELD1>001</FIELD1>
            </P02>           
        </P01>
        <PS01 SEGMENT="1">
            <FIELD1>VALUE</FIELD1>
        </PS01>
    </LEAV>
</ROOT>

** output 2:**

<?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>
            <P04 SEGMENT="1">
                <B_DOC>new value</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>            
        </P01>
        <P01 SEGMENT="1">
            <FIELD1>20</FIELD1>
            <FIELD2>1</FIELD2>
            <P02 SEGMENT="1">
                <FIELD1>001</FIELD1>
            </P02>
        </P01>
        <P01 SEGMENT="1">
            <FIELD1>30</FIELD1>
            <FIELD2>1</FIELD2>
            <P02 SEGMENT="1">
                <FIELD1>001</FIELD1>
            </P02>           
        </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="@*, K18/preceding-sibling::*[not(self::K18)]"/>
      <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:copy>
  </xsl:template>
  
  <xsl:template match="P01">
    <xsl:copy>
      <xsl:apply-templates select="@*, TP1/preceding-sibling::*[not(self::TP1)]"/>
      <xsl:for-each-group select="TP1" composite="yes" group-by="ID, RAS">
        <xsl:copy>
          <xsl:apply-templates select="@*, ID, RAS"/>
          <xsl:for-each-group select="current-group()!* except (current-group()!(ID, RAS))" composite="yes" group-by="node-name(), @*, *">
            <xsl:sequence select="."/>
          </xsl:for-each-group>
        </xsl:copy>
      </xsl:for-each-group>
      <xsl:apply-templates select="TP1/following-sibling::*[not(self::TP1)]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:output method="xml" indent="yes"/>

  <xsl:mode on-no-match="shallow-copy"/>

</xsl:stylesheet>

Solution

  • Try to replace the

     <xsl:apply-templates select="@*, TP1/preceding-sibling::*[not(self::TP1)]"/>
    

    with

     <xsl:apply-templates select="@*, if (TP1) then (* except (TP1/(., following-sibling::*))) else *"/>
    

    and

      <xsl:apply-templates select="TP1/following-sibling::*[not(self::TP1)]"/>
    

    with

      <xsl:apply-templates select="if (TP1) then (* except (TP1/(., preceding-sibling::*))) else ()"/>