2

I have the following XML document

<a:rootElement xmlns:a="http://a/1" xmlns:b="http://b/1" xmlns:c="http://c/1">
 <child1 type="b:type"/>
 <child2 type="c:type"/>
</a:rootElement>

Now I want to change the URIs of the namespaces so I get the following result

<a:rootElement xmlns:a="http://a/2" xmlns:b="http://b/2" xmlns:c="http://c/2">
 <child1 type="b:type"/>
 <child2 type="c:type"/>
</a:rootElement>

Nothing else should change. I tried it with the following stylesheet.

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:a="http://a/2"
xmlns:b="http://b/2"
xmlns:c="http://c/2" >

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

<xsl:template match="/*">
    <xsl:element name="{name()}" namespace="{namespace-uri()}">
        <xsl:copy-of select="document('')/*/namespace::*[name()='a']"/>
        <xsl:copy-of select="document('')/*/namespace::*[name()='b']"/>
        <xsl:copy-of select="document('')/*/namespace::*[name()='c']"/>
        <xsl:copy-of select="node()|@*"/>
    </xsl:element>
</xsl:template>

I get the following wrong output.

<a_0:rootElement xmlns:a_0="http://a/1" xmlns:a="http://a/2" xmlns:b="http://b/2" xmlns:c="http://c/2">
 <child1 type="b:type" xmlns:a="http://a/1" xmlns:b="http://b/1" xmlns:c="http://c/1"/>
 <child2 type="c:type" xmlns:a="http://a/1" xmlns:b="http://b/1" xmlns:c="http://c/1"/>
</a_0:rootElement>

I tried a few other ways too but also without the desired output. Is it even possible to change the namespaces in this way with XSLT?

Thanks for any advice

5
  • Is there a reason you don't do this with a simple text replace on the first line? It seems like that would be a LOT easier than trying to use XSLT to do it. Commented Jan 17, 2012 at 14:40
  • I have to change much more in the XML structure but this is the only task I could not solve. The transformation is a part of a model upgrader. Commented Jan 17, 2012 at 14:52
  • Can't you do both? Change the namespaces with the text replace, then apply your XSLT to do the rest? Given that you're effectively changing the namespace of most if not all elements in the document, an XSLT that does it all in one is likely to be a LOT more complicated. Commented Jan 17, 2012 at 14:54
  • I thought it would be easier to do it with a single XSLT but if it is easier and more efficient I could also use text replace before applying the XSLT. But is it even possible to change the namespaces in this way with XSLT? Commented Jan 17, 2012 at 14:57
  • Well, it does depend on where your XML's coming from. If it's just stored as text, then there's absolutely nothing stopping you modifying that text any way you like before actually using it as XML. Commented Jan 17, 2012 at 15:10

2 Answers 2

1

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:a1="http://a/1"
 xmlns:b1="http://b/1"
 xmlns:c1="http://c/1"
 xmlns:a="http://a/2"
 xmlns:b="http://b/2"
 xmlns:c="http://c/2"
>
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vDoc" select="document('')/*"/>

 <xsl:variable name="vnsA" select="$vDoc/namespace::*[name()='a1']"/>
 <xsl:variable name="vnsB" select="$vDoc/namespace::*[name()='b1']"/>
 <xsl:variable name="vnsC" select="$vDoc/namespace::*[name()='c1']"/>
 <xsl:variable name="vnsA2" select="$vDoc/namespace::*[name()='a']"/>
 <xsl:variable name="vnsB2" select="$vDoc/namespace::*[name()='b']"/>
 <xsl:variable name="vnsC2" select="$vDoc/namespace::*[name()='c']"/>

 <xsl:template match="*">
   <xsl:variable name="vNS" select="namespace-uri()"/>

     <xsl:variable name="vnewNS">
         <xsl:choose>
          <xsl:when test="$vNS = $vnsA">
           <xsl:value-of select="$vnsA2"/>
          </xsl:when>
          <xsl:when test="$vNS = $vnsB">
           <xsl:value-of select="$vnsB2"/>
          </xsl:when>
          <xsl:when test="$vNS = $vnsC">
           <xsl:value-of select="$vnsC2"/>
          </xsl:when>
         </xsl:choose>
     </xsl:variable>

     <xsl:element name="{name()}" namespace="{$vnewNS}">
      <xsl:copy-of select=
      "namespace::*[not(contains('|a|b|c|', concat('|', name(), '|')))]
      "/>
      <xsl:copy-of select="namespace::*[name() = 'a' and not(. = $vnsA)]"/>
      <xsl:copy-of select="namespace::*[name() = 'b' and not(. = $vnsB)]"/>
      <xsl:copy-of select="namespace::*[name() = 'c' and not(. = $vnsC)]"/>

      <xsl:if test="namespace::*[name() = 'a' and . = $vnsA]">
       <xsl:copy-of select="$vnsA2"/>
      </xsl:if>
      <xsl:if test="namespace::*[name() = 'b' and . = $vnsB]">
       <xsl:copy-of select="$vnsB2"/>
      </xsl:if>
      <xsl:if test="namespace::*[name() = 'c' and . = $vnsC]">
       <xsl:copy-of select="$vnsC2"/>
      </xsl:if>

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

</xsl:stylesheet>

when applied on the provided XML document:

<a:rootElement
 xmlns:a="http://a/1"
 xmlns:b="http://b/1"
 xmlns:c="http://c/1">

    <child1 type="b:type"/>
    <child2 type="c:type"/>
</a:rootElement>

produces the wanted, correct result:

<a:rootElement xmlns:a="http://a/2" xmlns:b="http://b/2" xmlns:c="http://c/2">
   <child1 type="b:type"/>
   <child2 type="c:type"/>
</a:rootElement>
Sign up to request clarification or add additional context in comments.

Comments

0

I think Dimitre adequately proved my point that any XSLT solution is going to be a lot more complicated than simply doing a text-replace on the first line of your XML document before processing it with XSLT.

Elements of different namespaces are technically completely different elements. Because XML technology considers them to be different, you actually have to handle and process each one, even though they'll appear identical when output.

Exactly how you do it depends on the platform you're using, but a simple 'find the first occurrence of http://a/1 and replace it with http://a/2 should do the job, for each namespace.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.