Dynamically change namespace in xsl

1.3k Views Asked by At

A little tricky to explain what I am trying to do, so I will use an over-simplified example.

I have three Xsl-templates: A.xsl, B.xsl and Common.xsl

Both A.xsl and B.xsl use "xsl:include" to include Common.xsl. A.xsl looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tns="http://MyNamespaceForA" >
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:include href="Common.xslt"/>
    <xsl:template match="/">
        <tns:RootA>
            <xsl:apply-templates select="Root" />
        </tns:RootA>
    </xsl:template>
</xsl:stylesheet>

B.xls looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tns="http://MyNamespaceForB" >
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:include href="Common.xslt"/>
    <xsl:template match="/">
        <tns:RootB>
            <xsl:apply-templates select="Root" />
        </tns:RootB>
    </xsl:template>
</xsl:stylesheet>

Finally, Common.xls looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="Root">
        <tns:Element>
            <tns:SubElement>
            </tns:SubElement>
        </tns:Element>
    </xsl:template>
</xsl:stylesheet>

So, A and B look different, they use the same namespace prefix (tns) but the namespaces have different values. A and B includes Common.xsl, which also uses the tns namespace.

In other words, I want the tns namespace in Common.xsl to take the value of the "calling", i.e. A or B, xsl-file.

However, this doesn't work when using XslCompiledTransform, it complains that tns is an undeclared namespace when in Common.xsl. If I declare the tns namespace in Common.xsl, I need to use either http://MyNamespaceForA or http://MyNamespaceForB.

Let's say I declare tns as http://MyNamespaceForB in Common.xsl. Now, when I use A.xsl, there will be conflicting values of the tns namespaces. The effect will be that the XML elements generated by Common.xsl will have an explicit namespace value of http://MyNamespaceForB. That won't work, of course.

Hope I am somewhat clear what I am trying to do. In short, I want to have the "calling" xsl-file dictate the value of a namespace in an included xsl-file.

Any ideas? /Fredrik

1

There are 1 best solutions below

0
On

If you use a direct namespace in Common.xslt you MUST declare the namespace in the same file, otherwise it won't be valid XML - no way the XSLT processor can load it.

The solution is to use xsl:element to create the elements and use a variable for the target namespace

A.xslt:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="targetNS" select="'http://MyNamespaceForA'"/>

  <xsl:include href="common.xslt"/>

  <xsl:template match="/">
    <xsl:element name="RootA" namespace="{$targetNS}">
      <xsl:apply-templates select="Root"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Common.xslt:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="Root">
    <xsl:element name="Element" namespace="{$targetNS}">
      <xsl:element name="SubElement" namespace="{$targetNS}">
      </xsl:element>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>