Peter
Peter

Reputation: 1796

copy subnode with matching position numbers

I have the following XML and want to copy <itemInformation> elements to be below <orderLineItem> elements:

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<order>
    <orderLineItem>1
        <requestedQuantity>
            <value>2</value>
        </requestedQuantity>
        <tradeItemIdentification>
            <gtin>07610400013192</gtin>
        </tradeItemIdentification>
    </orderLineItem>
    <orderLineItem>2
        <requestedQuantity>
            <value>1</value>
        </requestedQuantity>
        <tradeItemIdentification>
            <gtin>07610400014632</gtin>
        </tradeItemIdentification>
    </orderLineItem>
    <extension>
        <orderExtension>
            <itemInformation>1
                <tradeItemIdentification>
                    <gtin>07610400013192</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
            <itemInformation>2
                <tradeItemIdentification>
                    <gtin>07610400014632</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
        </orderExtension>
    </extension>
</order>
</Envelope>

I came up with the following XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

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

<xsl:template match="order/orderLineItem">
    <xsl:copy>
        <xsl:apply-templates select="@*" />

        <xxxx>
            <xsl:apply-templates select="parent::order/extension/orderExtension/itemInformation
                [normalize-space(text())=normalize-space(ancestor::order/orderLineItem/text())]"/>
        </xxxx>
        <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

I am expecting this target XML:

<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<order>
    <orderLineItem>
        <xxxx>
            <itemInformation>1
                <tradeItemIdentification>
                    <gtin>07610400013192</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
        </xxxx>1
        <requestedQuantity>
            <value>2</value>
        </requestedQuantity>
        <tradeItemIdentification>
            <gtin>07610400013192</gtin>
        </tradeItemIdentification>
    </orderLineItem>
    <orderLineItem>
        <xxxx>
            <itemInformation>2
                <tradeItemIdentification>
                    <gtin>07610400014632</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
        </xxxx>2
        <requestedQuantity>
            <value>1</value>
        </requestedQuantity>
        <tradeItemIdentification>
            <gtin>07610400014632</gtin>
        </tradeItemIdentification>
    </orderLineItem>
    <extension>
        <orderExtension>
            <itemInformation>1
                <tradeItemIdentification>
                    <gtin>07610400013192</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
            <itemInformation>2
                <tradeItemIdentification>
                    <gtin>07610400014632</gtin>
                </tradeItemIdentification>
                <grossCost>
                    <currencyCode>
                        <currencyISOCode>ZAR</currencyISOCode>
                    </currencyCode>
                    <monetaryAmount>243.60</monetaryAmount>
                </grossCost>
            </itemInformation>
        </orderExtension>
    </extension>
</order>
</Envelope>

But in the second <orderLineItem> element I am getting the first <itemInformation> again instead of the second one. So I have to match <itemInformation> 1, 2,... with <orderLineItem> 1, 2,... I just can't figure out the XPATH I need to use for that.

Upvotes: 0

Views: 50

Answers (1)

Carlo Cannas
Carlo Cannas

Reputation: 3262

The right XPath expression is the following:

../extension/orderExtension/itemInformation[normalize-space(text())=normalize-space(current()/text())]

The problem with yours is that you are messing up with the context node, remember that any relative path expression in the predicate (the expression you put between the brackets) refers to the current element, the one that will be picked in case the expression evaluates to true. The current function is defined by XSLT, and refers to its context element, which is the same context element of the whole XPath (but it changes in the predicates).

I also replaced the parent::order step with its abbreviated syntax ..

Upvotes: 1

Related Questions