Search code examples
xsltnodes

Adding missing nodes to XML via XSLT


I'm using a tool to import XML files into Dynamics NAV, but some parties providing the XML files skip empty nodes. My tool (external) can not handle those situation so I want to include XSLT to add the missing nodes. The xslt works fine for 1 node, but adding multiple nodes does not work. So I must be doing something wrong.

I'm building an integration to Dynamics NAV to insert Sales Orders. The orders are delivered from multiple parties using a XML file. However some of the parties providing the XML do not list all nodes in their XML file, they skip the empty ones. I'm using a tool build within Dynamics NAV (Add-on from other vendor) to import those files. However some XML files go wrong because of the fact that some (empty) nodes are missing in the XML file. I know this is an issue within the add-on but I need a solution on short notice. So created an XSLT to add the missing nodes. It works fine with 1 missing node, but it is not able to add both missing nodes. I'm not that familiar with XSLT so most of the times it is trial & error. Perhaps someone can help me with this.

This is the XML file format that is provided, The nodes that are sometimes missing is the DeliveryParty node and the DeliveryAddress part.

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
  <Order>
    <Partner>
  <SenderEANCode>9999999999999</SenderEANCode>
      <RecipientEANCode>9999999999999</RecipientEANCode>
    </Partner>
    <OrderHeader>
      <OrderVersion>008</OrderVersion>
      <OrderTypeCode>220</OrderTypeCode>
      <Document>
        <DocumentNumber>34034040</DocumentNumber>
        <Date>2019-04-18</Date>
      </Document>
      <DeliveryDate>2019-04-24</DeliveryDate>
      <CompleteDelivery>YES</CompleteDelivery>
      <Supplier>9999999999999</Supplier>
      <Buyer>9999999999999</Buyer>
      <Invoicee>9999999999999</Invoicee>
      <DeliveryParty>9999999999999</DeliveryParty>
      <DeliveryAddress>
        <DeliveryName>Private     Customer</DeliveryName>
        <DeliveryStreet>Teststraat</DeliveryStreet>
        <DeliveryCity>TestCity</DeliveryCity>
        <DeliveryCountry>NL</DeliveryCountry>
        <DeliveryTelNo></DeliveryTelNo>
        <DeliveryEmail>[email protected]</DeliveryEmail>
      </DeliveryAddress>
    </OrderHeader>
    <OrderLine>
      <LineItemNumber>1</LineItemNumber>
      <GTIN>9999999999999</GTIN>
      <OrderedQuantity>
        <Quantity>260</Quantity>
      </OrderedQuantity>
    </OrderLine>
  </Order>
</Orders>

Sometimes the DeliveryParty node is missing and other times the DeliveryAddress part including subnodes is missing. I created the following XSLT to add those nodes but as it is trail and error I need some help to fix this. I'm a novice to XSLT, I can so some small changes but I do not use it frequently so knowledge is fading away quickly.

<?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"/>

<xsl:template match="@*|node()">
   <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
</xsl:template>

<xsl:template match="Orders/Order/OrderHeader[not(DeliveryParty)]">
  <xsl:copy-of select="*"/>  
  <DeliveryParty/>
</xsl:template>

<xsl:template match="Orders/Order/OrderHeader[not(//DeliveryAddress)]">
 <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
     <DeliveryAddress>
       <DeliveryName></DeliveryName>
       <DeliveryStreet></DeliveryStreet>
       <DeliveryPostalCode></DeliveryPostalCode>
       <DeliveryCity></DeliveryCity>
       <DeliveryCountry></DeliveryCountry>
       <DeliveryTelNo></DeliveryTelNo>
       <DeliveryEmail></DeliveryEmail>
     </DeliveryAddress> 
   </xsl:copy>
</xsl:template>

With above mentioned XSLT the DeliveryAddress node with it's subnodes is added but the deliveryparty is not.

When the file is delivered like this:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
  <Order>
    <Partner>
  <SenderEANCode>9999999999999</SenderEANCode>
      <RecipientEANCode>9999999999999</RecipientEANCode>
    </Partner>
    <OrderHeader>
      <OrderVersion>008</OrderVersion>
      <OrderTypeCode>220</OrderTypeCode>
      <Document>
        <DocumentNumber>34034040</DocumentNumber>
        <Date>2019-04-18</Date>
      </Document>
      <DeliveryDate>2019-04-24</DeliveryDate>
      <CompleteDelivery>YES</CompleteDelivery>
      <Supplier>9999999999999</Supplier>
      <Buyer>9999999999999</Buyer>
      <Invoicee>9999999999999</Invoicee>
    </OrderHeader>
    <OrderLine>
      <LineItemNumber>1</LineItemNumber>
      <GTIN>9999999999999</GTIN>
      <OrderedQuantity>
        <Quantity>260</Quantity>
      </OrderedQuantity>
    </OrderLine>
  </Order>
</Orders>

The outcome should be this:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<Orders>
  <Order>
    <Partner>
  <SenderEANCode>9999999999999</SenderEANCode>
      <RecipientEANCode>9999999999999</RecipientEANCode>
    </Partner>
    <OrderHeader>
      <OrderVersion>008</OrderVersion>
      <OrderTypeCode>220</OrderTypeCode>
      <Document>
        <DocumentNumber>34034040</DocumentNumber>
        <Date>2019-04-18</Date>
      </Document>
      <DeliveryDate>2019-04-24</DeliveryDate>
      <CompleteDelivery>YES</CompleteDelivery>
      <Supplier>9999999999999</Supplier>
      <Buyer>9999999999999</Buyer>
      <Invoicee>9999999999999</Invoicee>
      <DeliveryParty></DeliveryParty>
      <DeliveryAddress>
        <DeliveryName></DeliveryName>
        <DeliveryStreet></DeliveryStreet>
        <DeliveryCity></DeliveryCity>
        <DeliveryCountry></DeliveryCountry>
        <DeliveryTelNo></DeliveryTelNo>
        <DeliveryEmail></DeliveryEmail>
      </DeliveryAddress>
    </OrderHeader>
    <OrderLine>
      <LineItemNumber>1</LineItemNumber>
      <GTIN>9999999999999</GTIN>
      <OrderedQuantity>
        <Quantity>260</Quantity>
      </OrderedQuantity>
    </OrderLine>
  </Order>
</Orders>

Solution

  • How about:

    XSLT 1.0

    <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="*"/>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="OrderHeader">
        <xsl:copy>
            <xsl:apply-templates/>
            <xsl:if test="not(DeliveryParty)">
                <DeliveryParty/>
            </xsl:if>
            <xsl:if test="not(DeliveryAddress)">
                <DeliveryAddress>
                    <DeliveryName/>
                    <DeliveryStreet/>
                    <DeliveryPostalCode/>
                    <DeliveryCity/>
                    <DeliveryCountry/>
                    <DeliveryTelNo/>
                    <DeliveryEmail/>
                </DeliveryAddress>
            </xsl:if>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>