I´ve to copy a node (Set_Item/ForwarderReferenceNumber) in case of mupltiplied and than remove the node "Set_Item" where we have taken the value from.
To find the same Set_Item (but different ForwarderReferenceNumer) we have to use "Set_Item/GTIN"). -> So in my example, we can see that "GTIN" of "LineNum" 2 and 7 is identical (555) and now we have to copy the "Set_Item/ForwarderReferenceNumber" from Line 7 below the "Set_Item/ForwarderReferenceNumber" of Line 2 and than remove the complete node of "Set_Item" with "LineNum" 7.
example of XML:
<OSTRPT>
<LineInformation>
<Set_Item>
<LineNum>2</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>666</ForwarderReferenceNumber>
<Date>2</Date>
</Set_Item>
<Set_Item>
<LineNum>7</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>777</ForwarderReferenceNumber>
<Date>3</Date>
</Set_Item>
</LineInformation>
</OSTRPT>
This should be the correct output:
<OSTRPT>
<LineInformation>
<Set_Item>
<LineNum>2</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>666</ForwarderReferenceNumber>
<ForwarderReferenceNumber>777</ForwarderReferenceNumber>
<Date>2</Date>
</Set_Item>
</LineInformation>
</OSTRPT>
Here is my current xslt. (But no solution for this case)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<xsl:choose>
<xsl:when test="//LineInformation[not(Item)] and //LineInformation[not(Set_Item)]"></xsl:when>
<xsl:when test="not(//Set_Item) and not(//Item)"></xsl:when>
<xsl:otherwise> <xsl:apply-templates select="@* | node()"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:key name="header_text" match="HeaderText" use="Text"/>
<xsl:key name="line_text" match="LineText" use="concat(../LineNum, '|', Text)"/>
<xsl:key name="allowance_charge_header" match="AllowanceOrCharge_Header" use="concat(Code, '|', Amount)"/>
<xsl:key name="allowance_charge_line" match="AllowanceOrCharge_Line" use="concat(../LineNum, '|', Code, '|', Amount)"/>
<xsl:key name="packing_slip" match="ItemDeliveryInformation" use="concat(../LineNum, '|', PackingSlipId, '|', DeliveryDate, '|', DeliveredQuantity)"/>
<xsl:key name="item" match="Item" use="concat(LineNum, '|', GTIN, '|', SupplierArticleNumber, '|', Quantity)"/>
<xsl:key name="set_item" match="Set_Item" use="concat(LineNum, '|', GTIN, '|', SupplierArticleNumber, '|', ForwarderReferenceNumber)"/>
<xsl:key name="WeightAndVolume" match="WeightAndVolume" use="Weight_RecId"/>
<xsl:key name="ContainerInformation" match="ContainerInformation" use="SSCC_RecId"/>
<xsl:key name="ContainerInformation_Set_Item" match="ContainerInformation_Set_Item" use="SSCC"/>
<!-- Identity-Template für die nicht explizit benannten Elemente -->
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//Forwarder/Code[//Forwarder/Country = 'DE' and //Forwarder/Name[contains(., 'GLS')]]">
<Code>5</Code>
</xsl:template>
<!-- Entfernt alles nach und inkl. "_" -->
<xsl:template match="GLN[contains(., '_')]">
<GLN><xsl:value-of select="substring-before(., '_')"/></GLN>
</xsl:template>
<!-- Entfernt alles nach und inkl. "_" -->
<xsl:template match="Recipient[contains(., '_')]">
<Recipient><xsl:value-of select="substring-before(., '_')"/></Recipient>
</xsl:template>
<xsl:template match="ItemDeliveryInformation_Set_Item">
<xsl:element name="ItemDeliveryInformation">
<!--copy all other nodes-->
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="Set_Item">
<xsl:element name="Item">
<!--copy all other nodes-->
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="RFF_Line_Set_Item">
<xsl:element name="RFF_Line">
<!--copy all other nodes-->
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="ContainerInformation_Set_Item">
<xsl:element name="ContainerInformation">
<!--copy all other nodes-->
<xsl:apply-templates select="@* | node()"/>
</xsl:element>
</xsl:template>
<!-- Differenz zwischen bestellter und gelieferter Menge -->
<xsl:template match="Item/ItemDeliveryInformation[. != '']">
<xsl:copy>
<!--copy all other nodes-->
<xsl:apply-templates select="@* | node()"/>
<QtyDifference>
<xsl:value-of select="format-number((./DeliveredQuantity - ../Ordered), '#0.00')"/>
</QtyDifference>
</xsl:copy>
</xsl:template>
<!-- das aufzurufende Template: OrderResponseReference Teil 1-->
<xsl:template name="last-substring-after">
<xsl:param name="search"/>
<xsl:param name="string"/>
<xsl:variable name="result" select="substring-after($string, $search)"/>
<xsl:choose>
<xsl:when test="contains($result, $search)">
<xsl:call-template name="last-substring-after">
<xsl:with-param name="search" select="$search"/>
<xsl:with-param name="string" select="$result"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- das aufzurufende Template: OrderResponseReference Teil 2-->
<xsl:template match="OrderResponseReference[contains (., '/')]">
<OrderResponseReference>
<xsl:call-template name="last-substring-after">
<xsl:with-param name="search" select="'/'"/>
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</OrderResponseReference>
</xsl:template>
<xsl:template match="OrderResponseReference[not(contains (., '/'))]">
<OrderResponseReference><xsl:value-of select="." /></OrderResponseReference>
</xsl:template>
<xsl:template match="HeaderText[generate-id() != generate-id(key('header_text', Text)[1])]" />
<xsl:template match="LineText[generate-id() != generate-id(key('line_text', concat(../LineNum, '|', Text))[1])]" />
<xsl:template match="AllowanceOrCharge_Header[generate-id() != generate-id(key('allowance_charge_header', concat(Code, '|', Amount))[1])]" />
<xsl:template match="AllowanceOrCharge_Line[generate-id() != generate-id(key('allowance_charge_line', concat(../LineNum, '|', Code, '|', Amount))[1])]" />
<xsl:template match="ItemDeliveryInformation[generate-id() != generate-id(key('packing_slip', concat(../LineNum, '|', PackingSlipId, '|', DeliveryDate, '|', DeliveredQuantity))[1])]" />
<xsl:template match="Item[generate-id() != generate-id(key('item', concat(LineNum, '|', GTIN, '|', SupplierArticleNumber, '|', Quantity))[1])]" />
<xsl:template match="Set_Item[generate-id() != generate-id(key('set_item', concat(LineNum, '|', GTIN, '|', SupplierArticleNumber, '|', ForwarderReferenceNumber))[1])]" />
<xsl:template match="WeightAndVolume[generate-id() != generate-id(key('WeightAndVolume', Weight_RecId)[1])]" />
<xsl:template match="ContainerInformation[generate-id() != generate-id(key('ContainerInformation', SSCC_RecId)[1])]" />
<xsl:template match="ContainerInformation_Set_Item[generate-id() != generate-id(key('ContainerInformation_Set_Item', SSCC)[1])]" />
<!-- Delete 0 at PackagingQty -->
<xsl:template match="PackagingUnit[PackagingQty = '0.00' or AllowanceOrChargeAmount = '0']"/>
<!-- Delete 0 at ContainerInformation/SSCC_RecId -->
<xsl:template match="ContainerInformation/SSCC_RecId"/>
<!-- Delete 0 at ContainerInformation/SSCC_RecId -->
<xsl:template match="ContainerInformation_Set_Item/SSCC_RecId"/>
<!-- Delete 0 at WeightAndVolume/Weight_RecId -->
<xsl:template match="WeightAndVolume/Weight_RecId"/>
<!-- delete empty nodes -->
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(@*|*|comment()|processing-instruction()) and normalize-space()='']"/>
</xsl:stylesheet>
Here is the example with duplicate lines:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<OSTRPT>
<LineInformation>
<Set_Item>
<LineNum>4</LineNum>
<GTIN>4251431238524</GTIN>
<SupplierArticleNumber>95003G4103VS</SupplierArticleNumber>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<StatusDescription>Ware versendet</StatusDescription>
<StatusChangeDate>2024-04-04T14:47:56</StatusChangeDate>
<ForwarderReferenceNumber>090028350002745490</ForwarderReferenceNumber>
<ReferenceDate>2024-04-04T14:47:56</ReferenceDate>
</Set_Item>
<Set_Item>
<LineNum>4</LineNum>
<GTIN>4251431238524</GTIN>
<SupplierArticleNumber>95003G4103VS</SupplierArticleNumber>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<StatusDescription>Ware versendet</StatusDescription>
<StatusChangeDate>2024-04-04T14:48:06</StatusChangeDate>
<ForwarderReferenceNumber>090028350002745513</ForwarderReferenceNumber>
<ReferenceDate>2024-04-04T14:47:56</ReferenceDate>
</Set_Item>
<Set_Item>
<LineNum>4</LineNum>
<GTIN>4251431238524</GTIN>
<SupplierArticleNumber>95003G4103VS</SupplierArticleNumber>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<StatusDescription>Ware versendet</StatusDescription>
<StatusChangeDate>2024-04-04T14:48:06</StatusChangeDate>
<ForwarderReferenceNumber>090028350002745513</ForwarderReferenceNumber>
<ReferenceDate>2024-04-04T14:47:56</ReferenceDate>
</Set_Item>
</LineInformation>
</OSTRPT>
Thank you!
best regards Julian
Use a key and process all ForwarderReferenceNumbers in a "group" formed by that key:
const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="group" match="Set_Item" use="GTIN"/>
<xsl:template match="Set_Item[not(generate-id() = generate-id(key('group', GTIN)[1]))]"/>
<xsl:template match="ForwarderReferenceNumber">
<xsl:copy-of select=". | key('group', ../GTIN)/ForwarderReferenceNumber"/>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>`;
var domParser = new DOMParser();
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(domParser.parseFromString(xslt, 'application/xml'));
var resultDoc = xsltProcessor.transformToDocument(domParser.parseFromString(document.getElementById('xml').text, 'application/xml'));
console.log(resultDoc);
console.log(new XMLSerializer().serializeToString(resultDoc));
<script id="xml" type="application/xml">
<OSTRPT>
<LineInformation>
<Set_Item>
<LineNum>2</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>666</ForwarderReferenceNumber>
<Date>2</Date>
</Set_Item>
<Set_Item>
<LineNum>7</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>777</ForwarderReferenceNumber>
<Date>3</Date>
</Set_Item>
</LineInformation>
</OSTRPT>
</script>
If you need to eliminate duplicates from the ForwarderReferenceNumber as well, use a second key:
const xslt = `<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="gtin-group" match="Set_Item" use="GTIN"/>
<xsl:key name="gtin-ref-group" match="Set_Item" use="concat(GTIN, '|', ForwarderReferenceNumber)"/>
<xsl:template match="Set_Item[not(generate-id() = generate-id(key('gtin-group', GTIN)[1]))]"/>
<xsl:template match="ForwarderReferenceNumber">
<xsl:copy-of select=". | key('gtin-group', ../GTIN)[generate-id() = generate-id(key('gtin-ref-group', concat(GTIN, '|', ForwarderReferenceNumber))[1])]/ForwarderReferenceNumber"/>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>`;
var domParser = new DOMParser();
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(domParser.parseFromString(xslt, 'application/xml'));
var resultDoc = xsltProcessor.transformToDocument(domParser.parseFromString(document.getElementById('xml').text, 'application/xml'));
//console.log(resultDoc);
console.log(new XMLSerializer().serializeToString(resultDoc));
<script id="xml" type="application/xml">
<OSTRPT>
<LineInformation>
<Set_Item>
<LineNum>2</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>666</ForwarderReferenceNumber>
<Date>2</Date>
</Set_Item>
<Set_Item>
<LineNum>7</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>777</ForwarderReferenceNumber>
<Date>3</Date>
</Set_Item>
<Set_Item>
<LineNum>7</LineNum>
<GTIN>555</GTIN>
<Quantity>1.00</Quantity>
<MeasureUnit>PCE</MeasureUnit>
<DeliveryDate>2024-03-13</DeliveryDate>
<Status>24</Status>
<ForwarderReferenceNumber>777</ForwarderReferenceNumber>
<Date>3</Date>
</Set_Item>
</LineInformation>
</OSTRPT>
</script>