Hari
Hari

Reputation: 21

XSLT 1.0 Recursive template call in XML Transformation

The HandlingUnitItem (/HandlingUnit/HandlingUnitItem/HandlingUnit/HandlingUnitItem) in the XML path getting repeated in the XML output. I'm not able to figure out why it was getting repeated in the Output.

I'm attaching the Input XML, XSLT code and the Output XML for your reference. exslt:node-set($Contents) what's this Content variable is holding?

Input xml :

<?xml version="1.0" encoding="UTF-8"?>
<SHPMNT05>
    <IDOC BEGIN="1">
        <E1EDT20 SEGMENT="1">
            <TKNUM>0001960714</TKNUM>
            <SHTYP>DEFC</SHTYP>
            <ABFER>1</ABFER>
            <ABWST>1</ABWST>
            <BFART>2</BFART>
            <E1EDL20 SEGMENT="1">
                <VBELN>0076668882</VBELN>
                <BTGEW>1.601</BTGEW>
                <NTGEW>1.584</NTGEW>
                <GEWEI>KGM</GEWEI>
                <E1EDL37 SEGMENT="1">
                    <EXIDV>00000000000109126078</EXIDV>
                    <TARAG>0.001</TARAG>
                    <GWEIT>KGM</GWEIT>
                    <BRGEW>0.001</BRGEW>
                    <NTGEW>0.000</NTGEW>
                    <GWEIM>KGM</GWEIM>
                    <VHILM>PM-3215</VHILM>
                    <LAENG>0.000</LAENG>
                    <BREIT>0.000</BREIT>
                    <HOEHE>0.000</HOEHE>
                    <VHILM_KU>KC2</VHILM_KU>
                    <VEBEZ>VDA-KLT R 3215</VEBEZ>
                    <SMGKN>S</SMGKN>
                    <E1EDL38 SEGMENT="1">
                        <MAGRV_BEZ>Ben</MAGRV_BEZ>
                        <VEBEZ>VDA</VEBEZ>
                    </E1EDL38>
                    <E1EDL44 SEGMENT="1">
                        <VELIN>1</VELIN>
                        <VBELN>0076668882</VBELN>
                        <POSNR>000010</POSNR>
                        <VEMNG>1600.000</VEMNG>
                        <VEMEH>PCE</VEMEH>
                        <MATNR>8993601404</MATNR>
                        <WERKS>DCXX</WERKS>
                        <LGORT>S002</LGORT>
                    </E1EDL44>
                </E1EDL37>
                <E1EDL37 SEGMENT="1">
                    <EXIDV>00000000000109126079</EXIDV>
                    <TARAG>0.000</TARAG>
                    <GWEIT>KGM</GWEIT>
                    <BRGEW>1.614</BRGEW>
                    <NTGEW>1.614</NTGEW>
                    <GWEIM>KGM</GWEIM>
                    <VHILM>PM-9999</VHILM>
                    <LAENG>0.000</LAENG>
                    <BREIT>0.000</BREIT>
                    <HOEHE>0.000</HOEHE>
                    <EXIDV2>040578630025545457</EXIDV2>
                    <VEBEZ>**********************</VEBEZ>
                    <SMGKN>M</SMGKN>
                    <E1EDL38 SEGMENT="1">
                        <VHART_BEZ>Palette</VHART_BEZ>
                        <MAGRV_BEZ>Ben</MAGRV_BEZ>
                        <VEBEZ>**********************</VEBEZ>
                    </E1EDL38>
                    <E1EDL44 SEGMENT="1">
                        <VELIN>3</VELIN>
                        <EXIDV>00000000000109126078</EXIDV>
                    </E1EDL44>
                </E1EDL37>
            </E1EDL20>
        </E1EDT20>
    </IDOC>
</SHPMNT05>

XSLT Code :

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" extension-element-prefixes="exslt">
    <xsl:template match="/">
        <xsl:choose>
            <xsl:when test="/*/IDOC/E1EDT20">
                <xsl:for-each select="/*/IDOC/E1EDT20|/*/IDOC[E1EDL20]">
                    <xsl:for-each select="E1EDL20/E1EDL37[not(EXIDV=/*/IDOC/E1EDT20/E1EDL20/E1EDL37/E1EDL44/EXIDV)]">
                        <xsl:sort select="EXIDV"/>
                        <xsl:value-of select="EXIDV"/>
                        <xsl:call-template name="HUTree"/>
                    </xsl:for-each>
                </xsl:for-each>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="HUTree">
        <HandlingUnit>
            <xsl:attribute name="key">
                <xsl:value-of select="concat(VHILM,VHILM_KU,SMGKN,LAENG,BREIT,HOEHE,BRGEW,NTGEW,GWEIM)"/>
                <xsl:for-each select="E1EDL44|E1EDT43">
                    <xsl:sort select="VELIN" order="descending"/>
                    <xsl:sort select="MATNR"/>
                    <xsl:value-of select="concat(VELIN,EXIDV,MATNR,VBELN,POSNR,VEMNG,VEMEH,KDMAT)"/>
                </xsl:for-each>
            </xsl:attribute>
            <xsl:copy-of select="EXIDV|VHILM|VHILM_KU|SMGKN|LAENG|BREIT|HOEHE|BRGEW|NTGEW|GWEIM"/>
            <xsl:variable name="Contents">
                <xsl:for-each select="E1EDL44|E1EDT43">
                    <xsl:sort select="VELIN" order="descending"/>
                    <xsl:sort select="MATNR"/>
                    <xsl:variable name="VBELN" select="VBELN"/>
                    <xsl:variable name="POSNR" select="POSNR"/>
                    <HandlingUnitItem>
                        <xsl:copy-of select="VELIN|MATNR|VBELN|POSNR|VEMNG|VEMEH|KDMAT"/>
                        <xsl:copy-of select="/*/IDOC/E1EDT20/E1EDL20[VBELN=$VBELN]/E1EDL24[POSNR=$POSNR]/E1EDL41[QUALI='001']/BSTNR"/>
                        <xsl:for-each select="../../E1EDL20/E1EDL37[EXIDV=current()/EXIDV] | ../../../E1EDL20/E1EDL37[EXIDV=current()/EXIDV]">
                            <xsl:sort select="EXIDV"/>
                            <xsl:call-template name="HUTree"/>
                        </xsl:for-each>
                    </HandlingUnitItem>
                </xsl:for-each>
            </xsl:variable>
            <xsl:copy-of select="$Contents"/>
            <xsl:for-each select="exslt:node-set($Contents)/HandlingUnitItem[not(HandlingUnit/@key=preceding-sibling::HandlingUnitItem/HandlingUnit/@key)]">
                <HandlingUnitItem>
                    <xsl:copy-of select="VELIN|MATNR|VBELN|POSNR|VEMNG|VEMEH|KDMAT|BSTNR"/>
                    <xsl:if test="HandlingUnit">
                        <HandlingUnit>
                            <xsl:variable name="str">
                                <xsl:for-each select=".|following-sibling::HandlingUnitItem[HandlingUnit/@key=current()/HandlingUnit/@key]">
                                    <xsl:if test="position()!=1 and HandlingUnit/EXIDV!=preceding-sibling::HandlingUnitItem[HandlingUnit/@key=current()/HandlingUnit/@key][1]/HandlingUnit/EXIDV+1">
                                        <xsl:value-of select="'|'"/>
                                    </xsl:if>
                                    <xsl:value-of select="concat(HandlingUnit/EXIDV,',')"/>
                                </xsl:for-each>
                            </xsl:variable>
                            <OutputStr>
                                <xsl:value-of select="$str"/>
                            </OutputStr>
                        </HandlingUnit>
                    </xsl:if>
                </HandlingUnitItem>
            </xsl:for-each>
        </HandlingUnit>
    </xsl:template>
</xsl:stylesheet>

Output xml :

<?xml version='1.0' encoding='UTF-8' ?>
00000000000109126079<HandlingUnit key="PM-9999M0.0000.0000.0001.6141.614KGM300000000000109126078">
    <EXIDV>00000000000109126079</EXIDV>
    <BRGEW>1.614</BRGEW>
    <NTGEW>1.614</NTGEW>
    <GWEIM>KGM</GWEIM>
    <VHILM>PM-9999</VHILM>
    <LAENG>0.000</LAENG>
    <BREIT>0.000</BREIT>
    <HOEHE>0.000</HOEHE>
    <SMGKN>M</SMGKN>
    <HandlingUnitItem>
        <VELIN>3</VELIN>
        <HandlingUnit key="PM-3215KC2S0.0000.0000.0000.0010.000KGM1899360140400766688820000101600.000PCE">
            <EXIDV>00000000000109126078</EXIDV>
            <BRGEW>0.001</BRGEW>
            <NTGEW>0.000</NTGEW>
            <GWEIM>KGM</GWEIM>
            <VHILM>PM-3215</VHILM>
            <LAENG>0.000</LAENG>
            <BREIT>0.000</BREIT>
            <HOEHE>0.000</HOEHE>
            <VHILM_KU>KC2</VHILM_KU>
            <SMGKN>S</SMGKN>
            <HandlingUnitItem>
                <VELIN>1</VELIN>
                <VBELN>0076668882</VBELN>
                <POSNR>000010</POSNR>
                <VEMNG>1600.000</VEMNG>
                <VEMEH>PCE</VEMEH>
                <MATNR>8993601404</MATNR>
                <BSTNR>0057328890010</BSTNR>
            </HandlingUnitItem>
            <HandlingUnitItem>
                <VELIN>1</VELIN>
                <VBELN>0076668882</VBELN>
                <POSNR>000010</POSNR>
                <VEMNG>1600.000</VEMNG>
                <VEMEH>PCE</VEMEH>
                <MATNR>8993601404</MATNR>
                <BSTNR>0057328890010</BSTNR>
            </HandlingUnitItem>
        </HandlingUnit>
    </HandlingUnitItem>
    <HandlingUnitItem>
        <VELIN>3</VELIN>
        <HandlingUnit>
            <OutputStr>00000000000109126078,</OutputStr>
        </HandlingUnit>
    </HandlingUnitItem>
</HandlingUnit>

Upvotes: 2

Views: 64

Answers (1)

Heiko Thei&#223;en
Heiko Thei&#223;en

Reputation: 16666

Please confirm whether this simplified example captures what you want to achieve:

  • Flatten a recursive node structure into a node list. (Done by the <xsl:template match="a">, the output is collected into $flat.)
  • From that list, keep only the first node with a given key. (key('key', ...) returns the first node with a given key, generate-id is used to check whether this is the current node.)
<a key="1">
  <a key="2">
    <a key="1" />
  </a>
</a>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common">
  <xsl:key name="key" match="a" use="@key" />
  <xsl:template match="/">
    <xsl:variable name="flat">
      <xsl:apply-templates select="a" />
    </xsl:variable>
    <flat>
      <xsl:for-each
        select="exslt:node-set($flat)/a[generate-id()=generate-id(key('key',@key))]">
        <a key="{@key}" />
      </xsl:for-each>
    </flat>
  </xsl:template>
  <xsl:template match="a">
    <a key="{@key}" /> <!-- Your key attribute is more complex -->
    <xsl:apply-templates select="a" />
  </xsl:template>
</xsl:stylesheet>

The condition with key and generate-id is not only faster than preceding-sibling::*, it works even without storing the flattened list in a variable:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="key" match="a" use="@key" />
  <xsl:template match="/">
    <flat>
      <xsl:apply-templates select="a" />
    </flat>
  </xsl:template>
  <xsl:template match="a">
    <xsl:if test="generate-id()=generate-id(key('key',@key))">
      <a key="{@key}" />
    </xsl:if>
    <xsl:apply-templates select="a" />
  </xsl:template>
</xsl:stylesheet>

(This last trick may not be applicable to your case, because your key attribute is computed from many key properties. Or is it? Many properties that make up your key are in fact measures like weight or height, I cannot imagine that these are really key components.)

Upvotes: 0

Related Questions