TransWikia.com

XSLT transform: Conditional copy of a child node based on a set of criteria

Stack Overflow Asked by Shuaipeng Zhang on January 9, 2021

Relatively new to XSLT.

Here’s a sample XML:

<Shipments>
                <Shipment>
                 <ErpOrder>415580479</ErpOrder>
                    <Containers>
                        <ShippingContainer>
                            <TreeUnit>12211620</TreeUnit>
                            <WeightUm>L</WeightUm>
                        </ShippingContainer>
                    </Containers>
                </Shipment>
                <Shipment>
                    <ErpOrder>415580479</ErpOrder>
                </Shipment>
                 <Shipment>
                    <ErpOrder>5124516</ErpOrder>
                </Shipment>
</Shipments>

I want to copy the <Containers> node and all child tag within that node to any <Shipment> tag that matches the <ErpOrder> value and is missing the <Containers> node.

Output:

<Shipments>
            <Shipment>
             <ErpOrder>415580479</ErpOrder>
                <Containers>
                    <ShippingContainer>
                        <TreeUnit>12211620</TreeUnit>
                        <WeightUm>L</WeightUm>
                    </ShippingContainer>
                </Containers>
            </Shipment>
            <Shipment>
                <ErpOrder>415580479</ErpOrder>
                 <Containers>
                    <ShippingContainer>
                        <TreeUnit>12211620</TreeUnit>
                        <WeightUm>L</WeightUm>
                    </ShippingContainer>
                </Containers>
            </Shipment>
             <Shipment>
                <ErpOrder>5124516</ErpOrder>
            </Shipment>
</Shipments>

What I have so far, but I think is completely off [EDITED]:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="no" indent="yes" method="xml"/>

 <xsl:key name = "erpordermatch" match = "Shipment" use = "@ErpOrder"/>

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

<xsl:template match="Shipment[not(Containers)]">
     <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:for-each select="key('erpordermatch', @ErpOrder)">
            <xsl:if test="@ErpOrder = ErpOrder">
                <xsl:copy-of select="Containers"/>
            </xsl:if>
        </xsl:for-each>
 </xsl:copy>                
            
    </xsl:template>
</xsl:stylesheet>

One Answer

See if this works for you:

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="*"/>

<xsl:key name="ship-by-erp" match="Shipment" use="ErpOrder" />

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

<xsl:template match="Shipment[not(Containers)]">
    <xsl:copy>
        <xsl:apply-templates/> 
        <xsl:copy-of select="key('ship-by-erp', ErpOrder)/Containers"/>
    </xsl:copy>
</xsl:template>
  
</xsl:stylesheet>

Note that this results in the first Shipment being duplicated in the output (as shown also in your expected output). Are you sure you want this?

Answered by michael.hor257k on January 9, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP