简体   繁体   中英

XSLT - Check if two nodes are equal in a for-each loop

This is my XML:

<Shipment>
<AddressData>   
    <Sender>
        <PartnerID>123</PartnerID>
        <Name1>Test</Name1>         
    </Sender>
    <Receiver>
        <PartnerID>124</PartnerID>
        <Name1>Test 2</Name1>       
    </Receiver>
</AddressData>
<Packaging>
    <Package>
        <ID>1</ID>
        <Length>20</Length>
        <Width>20</Width>           
        <PackageType>Carton</PackageType>
    </Package>
    <Package>
        <ID>2</ID>
        <Length>20</Length>
        <Width>20</Width>           
        <PackageType>Carton</PackageType>
    </Package>
</Packaging>
<Items>
    <Item>
        <PackageID>1</PackageID>
        <Partnumber>1234</Partnumber>
        <Quantity>3</Quantity>
    </Item>
    <Item>
        <PackageID>1</PackageID>
        <Partnumber>1235</Partnumber>
        <Quantity>3</Quantity>
    </Item>
    <Item>
        <PackageID>1</PackageID>
        <Partnumber>1236</Partnumber>
        <Quantity>3</Quantity>
    </Item>
    <Item>
        <PackageID>2</PackageID>
        <Partnumber>1232</Partnumber>
        <Quantity>3</Quantity>
    </Item>
    <Item>
        <PackageID>2</PackageID>
        <Partnumber>12322</Partnumber>
        <Quantity>3</Quantity>
    </Item>
</Items>

What I'm trying to acomplish is to list the relevant items of each package directly in the Packaging XML field.

So that I will become the following result:

<Shipment>
<AddressData>
    <Sender>
        <PartnerID>123</PartnerID>
        <Name1>Test</Name1>         
    </Sender>
    <Receiver>
        <PartnerID>124</PartnerID>
        <Name1>Test 2</Name1>       
    </Receiver>
</AddressData>
<Packaging>
    <Package>
        <ID>1</ID>
        <Length>20</Length>
        <Width>20</Width>           
        <PackageType>Carton</PackageType>
        <Items>
            <Item>
                <PackageID>1</PackageID>
                <Partnumber>1234</Partnumber>
                <Quantity>3</Quantity>
            </Item>
            <Item>
                <PackageID>1</PackageID>
                <Partnumber>1235</Partnumber>
                <Quantity>3</Quantity>
            </Item>
            <Item>
                <PackageID>1</PackageID>
                <Partnumber>1236</Partnumber>
                <Quantity>3</Quantity>
            </Item>
        </Items>
    </Package>
    <Package>
        <ID>2</ID>
        <Length>20</Length>
        <Width>20</Width>           
        <PackageType>Carton</PackageType>
        <Items>
            <Item>
                <PackageID>2</PackageID>
                <Partnumber>1232</Partnumber>
                <Quantity>3</Quantity>
            </Item>
            <Item>
                <PackageID>2</PackageID>
                <Partnumber>12322</Partnumber>
                <Quantity>3</Quantity>
            </Item>
        </Items>
    </Package>
</Packaging>

</Shipment>

What I've tried to do is the following XSLT Tranformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>
<xsl:template match="Items">
</xsl:template>

<xsl:template match="Packaging">
    <Packaging>
        <xsl:for-each select="Package">
            <ID>
                <xsl:value-of select="ID"/>
            </ID>
            <Length>
                <xsl:value-of select="Length"/>
            </Length>
            <Width>
                <xsl:value-of select="Width"/>
            </Width>
            <PackageType>
                <xsl:value-of select="PackageType"/>
            </PackageType>
            <Items>
                <xsl:for-each select="../../Items/Item">
                    <xsl:if test="PackageID = ../../Packaging/Package/ID">
                    <Item>
                        <PackageID><xsl:value-of select="PackageID"/></PackageID>
                        <Partnumber><xsl:value-of select="Partnumber"/></Partnumber>
                        <Quantity><xsl:value-of select="Quantity"/></Quantity>
                    </Item>
                    </xsl:if>
                </xsl:for-each>
            </Items>
        </xsl:for-each>
    </Packaging>
</xsl:template>
</xsl:stylesheet>

My current XSLT Transformation isn't working because all Items regardless of the ID will be shown. So the if statement seems to be not working correctly.

Thanks

The issue is that as soon as you do xsl:for-each select="../../Items/Item" you are in a different context, and your previous context (of the Package) is effectively "forgotten".

What you can do is store the current package id in a variable before selecting the items, and use that in the compate

<Items>
    <xsl:variable name="packageId" select="ID" />
    <xsl:for-each select="../../Items/Item">
        <xsl:if test="PackageID = $packageId">
        <Item>
            <PackageID><xsl:value-of select="PackageID"/></PackageID>
            <Partnumber><xsl:value-of select="Partnumber"/></Partnumber>
            <Quantity><xsl:value-of select="Quantity"/></Quantity>
        </Item>
        </xsl:if>
    </xsl:for-each>
</Items>

In fact, you can put your condition in the select of the xsl:for-each rather than do an xsl:if

<xsl:variable name="packageId" select="ID" />
<xsl:for-each select="../../Items/Item[PackageID = $packageId]">

In this case, this condition could reference the current package node, with current()

<xsl:for-each select="../../Items/Item[PackageID = current()/ID]">

But having said that, it may be even better to use xsl:key here. Define a key like so (as a child of xsl:stylesheet )

<xsl:key name="items" match="Item" use="PackageID" />

Then, you can do this...

<xsl:for-each select="key('items', ID)">

In fact, you don't even need xsl:for-each here at all. Use xsl:apply-templates and take advantage of the identity template.

Try this simplified XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="items" match="Item" use="PackageID" />

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

<xsl:template match="Items" />

<xsl:template match="Package">
    <Package>
        <xsl:apply-templates />
        <Items>
          <xsl:apply-templates select="key('items', ID)" />
        </Items>
    </Package>
</xsl:template>
</xsl:stylesheet>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM