0

I'm new on xslt, I've received a simple request: Replace the default xml namespace by another one in an xml document using Xslt (1.0, I can't use 2.0).

I find an easy way of doing it but I've still have a bug I don't understand (Wich happens with XslCompiledTransform from .Net framework 4.5 and with Altova Xml Spy but not with the Xslt plugin from Notepad++ for example):

This is the input Xml:

<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:old.namespace">
    <Companies>
        <Company>
            <AuditedOn xmlns:d4p1="http://schemas.datacontract.org/2004/07/System">
                <d4p1:DateTime>2012-10-08T13:34:04.04Z</d4p1:DateTime>
                <d4p1:OffsetMinutes>0</d4p1:OffsetMinutes>
            </AuditedOn>
            <Name>CompanyNameTest</Name>
        </Company>
        <Company>
            <AuditedOn xmlns:d6p1="http://schemas.datacontract.org/2004/07/System">
                <d6p1:DateTime>2012-10-08T13:34:04.04Z</d6p1:DateTime>
                <d6p1:OffsetMinutes>0</d6p1:OffsetMinutes>
            </AuditedOn>
            <Name>CompanyNameTest2</Name>
        </Company>
    </Companies>
</Test>

My Xslt:

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

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

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

  <!-- Previous namespace -> current. No other changes required. -->
  <xsl:template match='previous:*'>
    <xsl:element name='{local-name()}'
                 namespace='urn:new.namespace'>
      <xsl:copy-of select='namespace::*[not(. = namespace-uri(current()))]' />
      <xsl:apply-templates select='@* | node()' />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

And the result:

<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="urn:new.namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Companies>
        <Company>
            <AuditedOn xmlns:d4p1="http://schemas.datacontract.org/2004/07/System">
                <d4p1:DateTime xmlns="urn:old.namespace">2012-10-08T13:34:04.04Z</d4p1:DateTime>
                <d4p1:OffsetMinutes xmlns="urn:old.namespace">0</d4p1:OffsetMinutes>
            </AuditedOn>
            <Name>CompanyNameTest</Name>
        </Company>
        <Company>
            <AuditedOn xmlns:d6p1="http://schemas.datacontract.org/2004/07/System">
                <d6p1:DateTime xmlns="urn:old.namespace">2012-10-08T13:34:04.04Z</d6p1:DateTime>
                <d6p1:OffsetMinutes xmlns="urn:old.namespace">0</d6p1:OffsetMinutes>
            </AuditedOn>
            <Name>CompanyNameTest2</Name>
        </Company>
    </Companies>
</Test>

As you can see, on the AuditedOn node (Which is a DateTimeOffset object), the transformation set again xmlns="urn:old.namespace" on every node. I don't understand why.

UPDATE:

So, my expected result is:

<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="urn:new.namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Companies>
    <Company>
        <AuditedOn xmlns:d4p1="http://schemas.datacontract.org/2004/07/System">
            <d4p1:DateTime>2012-10-08T13:34:04.04Z</d4p1:DateTime>
            <d4p1:OffsetMinutes>0</d4p1:OffsetMinutes>
        </AuditedOn>
        <Name>CompanyNameTest</Name>
    </Company>
    <Company>
        <AuditedOn xmlns:d6p1="http://schemas.datacontract.org/2004/07/System">
            <d6p1:DateTime>2012-10-08T13:34:04.04Z</d6p1:DateTime>
            <d6p1:OffsetMinutes>0</d6p1:OffsetMinutes>
        </AuditedOn>
        <Name>CompanyNameTest2</Name>
    </Company>
</Companies>
</Test>

Please, can someone help me?

5
  • You are copying in-scope namespaces with <xsl:copy>, you would need to replace that with <xsl:element name="{name()}" namespace="{namespace-uri()}">, however that way for instance the xmlns:i="http://www.w3.org/2001/XMLSchema-instance" would be lost. Commented Sep 17, 2015 at 16:09
  • What is the result that you're after? Commented Sep 17, 2015 at 16:17
  • I don't want the urn:old.namespace set as a default namespace on de DateTime and OffsetMinutes nodes. Commented Sep 18, 2015 at 9:43
  • Why use XSLT for this? You could easily just change the content of the xmlns=".." declaration using simple string manipulation. Commented Sep 18, 2015 at 11:06
  • You're right, that's what I've said. The Xslt is a part of the application requirements, they can put them in a folder to dynamically change the output of an SOA app. Commented Sep 18, 2015 at 11:35

1 Answer 1

2

I believe this will produce the result you're looking for (which you neglected to provide):

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:previous="urn:old.namespace">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<!-- old namespace -> new -->
<xsl:template match="previous:*">
    <xsl:element name="{local-name()}" namespace="urn:new.namespace">
        <xsl:copy-of select="namespace::*[not(. = 'urn:old.namespace')]" />
        <xsl:copy-of select="@*" />
        <xsl:apply-templates select="node()" />
    </xsl:element>
</xsl:template>

<!-- instead of identity transform template -->
<xsl:template match="*">
    <xsl:element name="{name()}" namespace="{namespace-uri()}">
        <xsl:copy-of select="@*" />
        <xsl:apply-templates select="node()" />
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Result

<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="urn:new.namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
   <Companies>
      <Company>
         <AuditedOn xmlns:d4p1="http://schemas.datacontract.org/2004/07/System">
            <d4p1:DateTime>2012-10-08T13:34:04.04Z</d4p1:DateTime>
            <d4p1:OffsetMinutes>0</d4p1:OffsetMinutes>
         </AuditedOn>
         <Name>CompanyNameTest</Name>
      </Company>
      <Company>
         <AuditedOn xmlns:d6p1="http://schemas.datacontract.org/2004/07/System">
            <d6p1:DateTime>2012-10-08T13:34:04.04Z</d6p1:DateTime>
            <d6p1:OffsetMinutes>0</d6p1:OffsetMinutes>
         </AuditedOn>
         <Name>CompanyNameTest2</Name>
      </Company>
   </Companies>
</Test>

Added note:

The above could be simplified to just:

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

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

<!-- old namespace -> new -->
<xsl:template match="*[namespace-uri() = 'urn:old.namespace']">
    <xsl:element name="{local-name()}" namespace="urn:new.namespace">
        <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

The result here would be:

<?xml version="1.0" encoding="UTF-8"?>
<Test xmlns="urn:new.namespace">
   <Companies>
      <Company>
         <AuditedOn>
            <d4p1:DateTime xmlns:d4p1="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:old.namespace">2012-10-08T13:34:04.04Z</d4p1:DateTime>
            <d4p1:OffsetMinutes xmlns:d4p1="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:old.namespace">0</d4p1:OffsetMinutes>
         </AuditedOn>
         <Name>CompanyNameTest</Name>
      </Company>
      <Company>
         <AuditedOn>
            <d6p1:DateTime xmlns:d6p1="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:old.namespace">2012-10-08T13:34:04.04Z</d6p1:DateTime>
            <d6p1:OffsetMinutes xmlns:d6p1="http://schemas.datacontract.org/2004/07/System" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:old.namespace">0</d6p1:OffsetMinutes>
         </AuditedOn>
         <Name>CompanyNameTest2</Name>
      </Company>
   </Companies>
</Test>

This result is semantically the same as the other one. The differences are merely cosmetic:

  • The namespace declaration xmlns:i="http://www.w3.org/2001/XMLSchema-instance" has been moved and (needlessly) replicated to other elements - however, this declaration is redundant anyway, so it doesn't matter where it appears, or if appears at all;

  • Some elements declare a default namespace as xmlns="urn:old.namespace" - however, no element is actually placed in this namespace.

Similarly, the result you have reported in the question is also semantically identical to the two above. The redundant default namespace declaration of xmlns="urn:old.namespace" are just that: redundant. No node in the entire result is actually placed in this namespace.

Any target application that parses the result with a standard-conformant XML parser should not be able to tell the differences between these three results.

Sign up to request clarification or add additional context in comments.

2 Comments

The first solution you provide seems better to me: We avoid to add unused old namespace (xmlns="urn:old.namespace") to nodes and we keep the other namespaces on the root node (I put de xmlns:i as a test, my exemple is just a dummy exemple from a greater xml with a lot of namespaces)
You're right. I apologize about that, I tried to be clear but I've lost some of my requirements by doing it.

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.