Use value from sibling with condition - xslt

I've been trying to get this to work for quite some time and I know I still have to watch the 22hrs I have left on my Udemy xsl class but it's not an inspiring teacher..
I want to search for a value higher up in the xml tree and use that value 'x times'. I want the value of /SHPMNT05/IDOC/E1EDT20/E1EDK33/E1EDT44/ABLAD
where /SHPMNT05/IDOC/E1EDT20/E1EDK33/E1EDT01/VBELN = /SHPMNT05/IDOC/E1EDT20/E1EDL20/VBELN
and use that in an extra /SHPMNT05/IDOC/E1EDT20/E1EDL20/ABLAD element
1 - I have as input xml
<?xml version="1.0" encoding="UTF-8"?>
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000287203</TKNUM>
<E1EDK33 SEGMENT="1">
<TSNUM>0001</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>1</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018667</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>2</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018668</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>3</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018666</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDL20 SEGMENT="1">
<VBELN>0081018666</VBELN>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018667</VBELN>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018668</VBELN>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
2 - I want
<?xml version="1.0" encoding="UTF-8"?>
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000287203</TKNUM>
<E1EDK33 SEGMENT="1">
<TSNUM>0001</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>1</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018667</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>2</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018668</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>3</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018666</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDL20 SEGMENT="1">
<VBELN>0081018666</VBELN>
***<ABLAD>3</ABLAD>***
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018667</VBELN>
***<ABLAD>1</ABLAD>***
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018668</VBELN>
***<ABLAD>2</ABLAD>***
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
3 - My xsl is
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- <xsl:template match="/SHPMNT05/IDOC/E1EDT20/E1EDK33/E1EDT01[(VBELN= following-sibling::E1EDT01/VBELN) or (VBELN= ../following-sibling::E1EDK33/E1EDT01/VBELN)]"/> -->
<xsl:template match="/SHPMNT05/IDOC/E1EDT20/E1EDL20/VBELN">
<xsl:copy-of select="."/>
<xsl:variable name="stopno" select="/SHPMNT05/IDOC/E1EDT20/E1EDK33/E1EDT01[VBELN=VBELN]/parent::node()/E1EDT44/ABLAD"/>
<ABLAD><xsl:value-of select="$stopno" /></ABLAD>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
</xsl:stylesheet>
4 - I get
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000287203</TKNUM>
<E1EDK33 SEGMENT="1">
<TSNUM>0001</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<ABLAD>1</ABLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018667</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<OBLAD>2</OBLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018668</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDK33 SEGMENT="1">
<TSNUM>0002</TSNUM>
<E1EDT44 SEGMENT="1">
<QUALI>001</QUALI>
</E1EDT44>
<E1EDT44 SEGMENT="1">
<QUALI>002</QUALI>
<OBLAD>3</OBLAD>
</E1EDT44>
<E1EDT01 SEGMENT="1">
<VBELN>0081018666</VBELN>
</E1EDT01>
</E1EDK33>
<E1EDL20 SEGMENT="1">
<VBELN>0081018666</VBELN>
<ABLAD>1</ABLAD>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018667</VBELN>
<ABLAD>1</ABLAD>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018668</VBELN>
<ABLAD>1</ABLAD>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
Do I need to use a 'for each' or something?
Kind regards,
Mike

<xsl:variable name="stopno" select="/SHPMNT05/IDOC/E1EDT20
/E1EDK33[E1EDT01/VBELN=current()]
/E1EDT44/ABLAD"/>
Note two points:
E1EDT01 is inside the square brackets, which saves the parent::node() step.
The VBELN element that was matched by the current template can be accessed as current().

Related

XSL double xml element

I hope somebody can tell me why I do get 2 E1EDT20 elements in my result? Must be doing a copy too many but I can't figure it out.
My input xml
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000287214</TKNUM>
<E1EDL20 SEGMENT="1">
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
My XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:copy-of select="*[not(self::E1EDL20)]"/>
<xsl:copy>
<xsl:for-each-group select="E1EDL20" group-adjacent="concat(ABLAD, '|', VKORG)" >
<CMR>
<xsl:copy-of select="current-group()"/>
</CMR>
</xsl:for-each-group>
</xsl:copy>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
My current result looks like:
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20>
<TKNUM>0000287214</TKNUM>
<E1EDT20>
<CMR>
<E1EDL20 SEGMENT="1">
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</CMR>
<CMR>
<E1EDL20 SEGMENT="1">
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
</CMR>
</E1EDT20>
</E1EDT20>
</IDOC>
</SHPMNT05>
Required result
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20>
<TKNUM>0000287214</TKNUM>
<CMR>
<E1EDL20 SEGMENT="1">
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</CMR>
<CMR>
<E1EDL20 SEGMENT="1">
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
</CMR>
</E1EDT20>
</IDOC>
</SHPMNT05>
https://xsltfiddle.liberty-development.net/bFksq1t
I'm almost there (thanks to your stack support :) ) must be doing something stupid here..
Kind regards,
Mike
Well, as you say, you have two xsl:copy:
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:copy-of select="*[not(self::E1EDL20)]"/>
<xsl:copy>

XSL to add wrapper element based on value change

I want to create a 'wrapper' / parent element around my E1EDL20 elements when some values change. To be able to more or less group on that new element in SAP PO.
When the value in ABLAD changes or when I have a new VKORG I need to create the wrapper element.
I can create the element but I want the E1EDL20 to be part of that new group.
https://xsltfiddle.liberty-development.net/nbL5Ydw/5
Input xml:
<?xml version="1.0" encoding="UTF-8"?>
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20 SEGMENT="1">
<TKNUM>0000287214</TKNUM>
<E1EDL20 SEGMENT="1">
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018723</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018711</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018712</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018708</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
<E1EDL20 SEGMENT="1">
<VBELN>0081018709</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
XSL:
<?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" omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:for-each select="//E1EDL20">
<xsl:variable name="stopno" select="ABLAD"/>
<xsl:variable name="salesorg" select="VKORG"/>
<xsl:if test = "not ($stopno = preceding-sibling::E1EDL20/ABLAD) or not ($salesorg = preceding-sibling::E1EDL20/VKORG)">
Grouping
<GROUPBY><xsl:value-of select="$stopno" />_<xsl:value-of select="$salesorg" />
</GROUPBY>
</xsl:if>
<xsl:copy>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Current result:
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20>
Grouping
<GROUPBY>1_0100</GROUPBY>
<E1EDL20>
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
Grouping
<GROUPBY>1_0210</GROUPBY>
<E1EDL20>
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
<E1EDL20>
<VBELN>0081018723</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
Grouping
<GROUPBY>2_0200</GROUPBY>
<E1EDL20>
<VBELN>0081018711</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
<E1EDL20>
<VBELN>0081018712</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
Grouping
<GROUPBY>3_0200</GROUPBY>
<E1EDL20>
<VBELN>0081018708</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
<E1EDL20>
<VBELN>0081018709</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</E1EDT20>
</IDOC>
</SHPMNT05>
Required result:
<SHPMNT05>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDT20>
<GROUPBY>
<E1EDL20>
<VBELN>0081018702</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</GROUPBY>
<GROUPBY>
<E1EDL20>
<VBELN>0081018703</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
<E1EDL20>
<VBELN>0081018723</VBELN>
<ABLAD>1</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0210</VKORG>
</E1EDL20>
</GROUPBY>
<GROUPBY>
<E1EDL20>
<VBELN>0081018711</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
</GROUPBY>
<GROUPBY>
<E1EDL20>
<VBELN>0081018712</VBELN>
<ABLAD>2</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</GROUPBY>
<GROUPBY>
<E1EDL20>
<VBELN>0081018708</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0200</VKORG>
</E1EDL20>
</GROUPBY>
<GROUPBY>
<E1EDL20>
<VBELN>0081018709</VBELN>
<ABLAD>3</ABLAD>
<VSTEL>EWM</VSTEL>
<VKORG>0100</VKORG>
</E1EDL20>
</GROUPBY>
</E1EDT20>
</IDOC>
</SHPMNT05>
As you can see the grouping on salesorg is not working correctly either because I would expect a new group for 0081018711 and 0081018712 (VKORG 0200 / VKORG 0100).
Kind regards,
Mike
It sounds like a use case for group-adjacent e.g.
<xsl:template match="E1EDT20">
<xsl:copy>
<xsl:for-each-group select="E1EDL20" group-adjacent="ABLAD || '|' || VKORG" >
<GROUPBY>
<xsl:copy-of select="current-group()"/>
</GROUPBY>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
(or, if that is an XSLT 2 processor, use group-adjacent="concat(ABLAD, '|', VKORG)", as the || operator is XPath/XSLT 3).

Keep main element after sorting with xsl

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>

Get previous value in for-each loop

I have the following XML:
<DELVRY07>
<IDOC BEGIN="1">
<E1EDL20 SEGMENT="1">
<E1EDL24 SEGMENT="1">
<POSNR>000010</POSNR>
<E1EDL37 SEGMENT="1">
<EXIDV>123</EXIDV>
<E1EDL38 SEGMENT="1">
<VHART_BEZ>SSCC Box</VHART_BEZ>
<MAGRV_BEZ>All materials</MAGRV_BEZ>
<VEBEZ>BOX - Parcel Express</VEBEZ>
</E1EDL38>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000010</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000020</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000030</POSNR>
</E1EDL44>
</E1EDL37>
</E1EDL24>
<E1EDL24 SEGMENT="1">
<POSNR>000020</POSNR>
<E1EDL37 SEGMENT="1">
<EXIDV>123</EXIDV>
<E1EDL38 SEGMENT="1">
<VHART_BEZ>SSCC Box</VHART_BEZ>
<MAGRV_BEZ>All materials</MAGRV_BEZ>
<VEBEZ>BOX - Parcel Express</VEBEZ>
</E1EDL38>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000010</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000020</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000030</POSNR>
</E1EDL44>
</E1EDL37>
</E1EDL24>
<E1EDL24 SEGMENT="1">
<POSNR>000030</POSNR>
<E1EDL37 SEGMENT="1">
<EXIDV>456</EXIDV>
<E1EDL38 SEGMENT="1">
<VHART_BEZ>SSCC Box</VHART_BEZ>
<MAGRV_BEZ>All materials</MAGRV_BEZ>
<VEBEZ>BOX - Parcel Express</VEBEZ>
</E1EDL38>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000010</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000020</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000030</POSNR>
</E1EDL44>
</E1EDL37>
</E1EDL24>
</E1EDL20>
</IDOC>
Where I want to remove all E1EDL37 segments which have the same EXIDV value as the previous one:
<DELVRY07>
<IDOC BEGIN="1">
<E1EDL20 SEGMENT="1">
<E1EDL24 SEGMENT="1">
<POSNR>000010</POSNR>
<E1EDL37 SEGMENT="1">
<EXIDV>123</EXIDV>
<E1EDL38 SEGMENT="1">
<VHART_BEZ>SSCC Box</VHART_BEZ>
<MAGRV_BEZ>All materials</MAGRV_BEZ>
<VEBEZ>BOX - Parcel Express</VEBEZ>
</E1EDL38>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000010</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000020</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000030</POSNR>
</E1EDL44>
</E1EDL37>
</E1EDL24>
<E1EDL24 SEGMENT="1">
<POSNR>000020</POSNR>
</E1EDL24>
<E1EDL24 SEGMENT="1">
<POSNR>000030</POSNR>
<E1EDL37 SEGMENT="1">
<EXIDV>456</EXIDV>
<E1EDL38 SEGMENT="1">
<VHART_BEZ>SSCC Box</VHART_BEZ>
<MAGRV_BEZ>All materials</MAGRV_BEZ>
<VEBEZ>BOX - Parcel Express</VEBEZ>
</E1EDL38>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000010</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000020</POSNR>
</E1EDL44>
<E1EDL44 SEGMENT="1">
<VELIN>1</VELIN>
<VBELN>3030043338</VBELN>
<POSNR>000030</POSNR>
</E1EDL44>
</E1EDL37>
</E1EDL24>
</E1EDL20>
</IDOC>
I tried this with the following XSLT conversion, but this will eliminate all E1EDL37 segments:
<xsl:stylesheet version="2.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">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<DELVRY07>
<IDOC BEGIN="1">
<xsl:copy-of select="//EDI_DC40"/>
<E1EDL20 SEGMENT="1">
<VBELN><xsl:value-of select="/DELVRY07/IDOC/E1EDL20/VBELN"/></VBELN>
<BTGEW><xsl:value-of select="/DELVRY07/IDOC/E1EDL20/BTGEW"/></BTGEW>
<GEWEI><xsl:value-of select="/DELVRY07/IDOC/E1EDL20/GEWEI"/></GEWEI>
<xsl:copy-of select="//E1ADRM1"/>
<xsl:copy-of select="//E1EDT13"/>
<xsl:for-each select="/DELVRY07/IDOC/E1EDL20/E1EDL24">
<E1EDL24 SEGMENT="1">
<xsl:variable name="position" select="."/>
<POSNR><xsl:value-of select="POSNR"/></POSNR>
<MATNR><xsl:value-of select="MATNR"/></MATNR>
<ARKTX><xsl:value-of select="ARKTX"/></ARKTX>
<LFIMG><xsl:value-of select="LFIMG"/></LFIMG>
<EAN11><xsl:value-of select="EAN11"/></EAN11>
<xsl:copy-of select="E1EDL41"/>
<xsl:variable name="pos" select="position()"/>
<xsl:variable name="preceding-group-member" select="current-group()[$pos - 1]"/>
<xsl:variable name="this-data" select="E1EDL37/EXIDV"/>
<xsl:variable name="preceding-data" select="$preceding-group-member/E1EDL37/EXIDV"/>
<xsl:if test="$this-data != $preceding-data">
<xsl:copy-of select="E1EDL37"/>
</xsl:if>
</E1EDL24>
</xsl:for-each>
</E1EDL20>
</IDOC>
</DELVRY07>
</xsl:template>
I can't figure out why this is and I've been searching for a solution several hours now. Has anyone an idea how to solve this?
Use a key to detect duplicates, similar to the Muenchian method:
<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:strip-space elements="*"/>
<xsl:key name="k1" match="E1EDL37" use="EXIDV" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- remove duplicate E1EDL37s -->
<xsl:template match="E1EDL37[not(count(. | key('k1', EXIDV)[1]) = 1)]"/>
</xsl:stylesheet>
You could simplify this using templates :
<xsl:stylesheet version="2.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">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="E1EDL37[EXIDV=preceding::E1EDL37[1]/EXIDV]"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/pNmC4Jk
You could use xsl:for-each-group with group-adjacent, and then select the first in the group
<xsl:for-each-group select="E1EDL37" group-adjacent="EXIDV">
<xsl:copy-of select="current-group()[1]"/>
</xsl:for-each-group>

XSLT 1.0: sorting nodes changes structure

I have the following simplified XML structure:
<?xml version="1.0" encoding="UTF-8" ?>
<INVOIC02>
<IDOC BEGIN="1">
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718221</IDTNR>
<KTEXT>Orange</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>Z03</QUALF>
<IDTNR>7610400013079</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718160</IDTNR>
<KTEXT>Pistache</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400181600</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718228</IDTNR>
<KTEXT>Strawberry</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182287</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718221</IDTNR>
<KTEXT>Orange</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182218</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718160</IDTNR>
<KTEXT>Pistache</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400181600</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718228</IDTNR>
<KTEXT>Strawberry</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182287</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDS01 SEGMENT="1">
<SUMID>001</SUMID>
<SUMME>6</SUMME>
</E1EDS01>
</IDOC>
</INVOIC02>
I need to sort the <E1EDP01 SEGMENT="1"> segments by their <IDTNR> number with QUALF='002'
So I came up with this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" version="1.0">
<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="IDOC">
<xsl:apply-templates>
<xsl:sort select="E1EDP19[QUALF='002']/IDTNR"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
But in my Output the <E1EDP01> Segments are sorted but are put at the end of my structure:
<?xml version="1.0" encoding="UTF-8"?>
<INVOIC02>
<EDI_DC40 SEGMENT="1">
<TABNAM>EDI_DC40</TABNAM>
</EDI_DC40>
<E1EDS01 SEGMENT="1">
<SUMID>001</SUMID>
<SUMME>6</SUMME>
</E1EDS01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718160</IDTNR>
<KTEXT>Pistache</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400181600</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718160</IDTNR>
<KTEXT>Pistache</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400181600</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718221</IDTNR>
<KTEXT>Orange</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>Z03</QUALF>
<IDTNR>7610400013079</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718221</IDTNR>
<KTEXT>Orange</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182218</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718228</IDTNR>
<KTEXT>Strawberry</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182287</IDTNR>
</E1EDP19>
</E1EDP01>
<E1EDP01 SEGMENT="1">
<MENGE>1.000</MENGE>
<E1EDP19 SEGMENT="1">
<QUALF>002</QUALF>
<IDTNR>000000000000718228</IDTNR>
<KTEXT>Strawberry</KTEXT>
</E1EDP19>
<E1EDP19 SEGMENT="1">
<QUALF>003</QUALF>
<IDTNR>7610400182287</IDTNR>
</E1EDP19>
</E1EDP01>
</INVOIC02>
The <E1EDS01> segment comes before the sorted segments but should be at the end. Why is that?
This happens because you sort everything and non-E1EDP01 elements are going to have empty sort keys.
instead, you could force their position, like:
<xsl:template match="IDOC">
<xsl:apply-templates select="EDI_DC40"/>
<xsl:apply-templates select="E1EDP01">
<xsl:sort select="E1EDP19[QUALF='002']/IDTNR"/>
</xsl:apply-templates>
<xsl:apply-templates select="E1EDS01"/>
</xsl:template>