Search code examples
xmlxsltxslt-1.0

XSLT 1.0 copy and remove node


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


Solution

  • 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>