replace namespace prefix and add attributes using XSLT

时间:2015-07-28 16:05:05

标签: xslt namespaces

suppose I have following XML as source:

<ns0:msg xmlns:ns0="namespace0">
    <ns0:hdr a_lot_of_attrs="value">
some nodes...
</ns0:hdr>
    <ns0:body>
        <ns0:data a_lot_of_attrs="value">
            <ns1:purchase_order xmlns:ns1="namespace1">some nodes...</ns1:purchase_order>
        </ns0:data>
    </ns0:body>
</ns0:msg>

And I need following XML as the result:

    <a:msg xmlns:a="namespace0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <a:hdr a_lot_of_attrs="value">
    some nodes...
    </a:hdr>
        <a:body>
            <a:data a_lot_of_attrs="value">
                <b:purchase_order 
 xsi:schemaLocation="filelocation"
xmlns:b="namespace1"  
xmlns:c="namespace2">some nodes...</b:purchase_order>
            </a:data>
        </a:body>
    </a:msg>

Basically I need just to replace the namespace prefix ns0 into a and ns1 into b. Further more, the root element <a:msg> as well as the <b:purchase_order> need to be added by some additional attributes.

My attemp is by using following XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="namespace0" 
xmlns:ns1="namespace1" 
xmlns:a="namespace0" 
xmlns:b="namespace1" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
exclude-result-prefixes="ns0 ns1">

<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

    <xsl:template match="/">
        <a:msg xmlns:msg="namespace1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <xsl:copy>
                <xsl:apply-templates select="ns0:msg/*"/>
            </xsl:copy>
        </a:msg>
    </xsl:template>

    <xsl:template match="/ns0:msg/ns0:body/ns0:data/ns1:purchase_order">
        <b:purchase_order xsi:schemaLocation="filelocation" xmlns:c="namespace2">
            <xsl:copy>
                <xsl:apply-templates select="node()"/>
            </xsl:copy>
        </b:purchase_order>
    </xsl:template>

    <xsl:template match="ns0:*">
        <xsl:element name="a:{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="ns1:*">
        <xsl:element name="b:{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="@*">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

It works fine so far, except the node <purchase_order> has been populated 2 times:

<a:msg xmlns:a="namespace0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <a:hdr a_lot_of_attrs="value">
some nodes...
</a:hdr>
    <a:body>
        <a:data a_lot_of_attrs="value">
            <b:purchase_order xmlns:b="namespace1" xmlns:c="namespace2" xsi:schemaLocation="filelocation">
                <ns1:purchase_order xmlns:ns0="namespace0" xmlns:ns1="namespace1">some nodes...</ns1:purchase_order>
            </b:purchase_order>
        </a:data>
    </a:body>
</a:msg>

I tried several times by tweaking the second <xsl:template> but could not get it right. Would someone pls advise where I get wrong here and how can I get this done?

Thanks a lot.

1 个答案:

答案 0 :(得分:0)

You current template that matches ns1:purchase_order contains an xsl:copy as well as the creation of the new b:purchase_order. Therefore you are copying the old node as well as creating a new one.

You can remove the xsl:copy from the template, like so:

<xsl:template match="ns1:purchase_order">
    <b:purchase_order xsi:schemaLocation="filelocation" xmlns:c="namespace2">
        <xsl:apply-templates select="node()"/>
    </b:purchase_order>
</xsl:template>

Note that you don't necessarily have to specify the full path in the template match. You would only really need to do this if you had a second ns1:purchase_order in the XML, at a different location, that you didn't want to match.