user3096771
user3096771

Reputation: 51

How to set a namespace name in XSLT via param

I need to set a namespace in an XSLT stylesheet at runtime. Here is my XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tdns="{$myNS}">
    <xsl:output method="xml" encoding="iso-8859-1" />

    <xsl:param name="myNS" required="yes" />

    <xsl:template match="*">

        <xsl:choose>
            <xsl:when test="name(.)='bugs'">
                <xsl:element name="tdns:bugs">

                    <xsl:for-each select="*">
                        <xsl:apply-templates select="." />
                    </xsl:for-each>
                </xsl:element>
            </xsl:when>
            <xsl:when test="name(.)='bug'">

                <xsl:element name="tdns:bug">
                    <xsl:for-each select="*">
                        <xsl:choose>
                            <xsl:when test="name(.)='Device' ">
                                <xsl:choose>
                                    <xsl:when test="string-length(../tdns:Device_Tmp) &gt; 0 ">
                                        <xsl:element name="tdns:Device">

                                            <xsl:value-of select="../tdns:Device_Tmp" />
                                        </xsl:element>
                                    </xsl:when>
                                    <xsl:otherwise>
                                        <xsl:element name="tdns:Device">
                                            <xsl:value-of select="." />
                                        </xsl:element>
                                    </xsl:otherwise>
                                </xsl:choose>
                                <xsl:element name="tdns:ShowParamValue">
                                    <xsl:value-of select="$myNS" />
                                </xsl:element>
                            </xsl:when>

                        </xsl:choose>
                    </xsl:for-each>
                </xsl:element>
            </xsl:when>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Input XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<bugs xmlns="http://my.com">
    <bug>
        <Device>
            <![CDATA[Dev1]]>
        </Device>
        <Device_Tmp>
            <![CDATA[Dev_tmp1]]>
        </Device_Tmp>
    </bug>
</bugs>

Java code

TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer(new StreamSource(
        new File(xsl)));
transformer.setParameter("myNS", "http://my.com");
transformer.transform(new StreamSource(new File(
        xml)),
        new StreamResult(System.out));

Output XML

<?xml version="1.0" encoding="iso-8859-1"?>
<tdns:bugs xmlns:tdns="http://my.com">
    <tdns:bug>
        <tdns:Device>
            Dev1
        </tdns:Device>
        <tdns:ShowParamValue>http://my.com</tdns:ShowParamValue>
    </tdns:bug>
</tdns:bugs> 

As you can see, XSLT doesn't know any ../tdns:Device_Tmp node , but if I set a static namespace:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tdns="http://my.com">
...

Then, the result is OK:

<?xml version="1.0" encoding="iso-8859-1"?>
<tdns:bugs xmlns:tdns="http://my.com">
    <tdns:bug>
        <tdns:Device>
            Dev_tmp1
        </tdns:Device>
        <tdns:ShowParamValue>http://my.com</tdns:ShowParamValue>
    </tdns:bug>
</tdns:bugs>

Please, tell me what I'm doing wrong.

Upvotes: 3

Views: 1554

Answers (2)

Ian Roberts
Ian Roberts

Reputation: 122414

Namespace declarations are not attributes, and are not treated as attribute value templates (in fact they're processed by the XML parser that parses the stylesheet document, before it gets anywhere near the XSLT processor).

You'll have to use <xsl:element> to create elements with the name and namespace attributes, in order to be able to specify the namespace dynamically. For matching the elements the easiest approach might be to ignore the namespaces entirely (since you're in XSLT 2.0 this is easy with *:localname).

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

    <xsl:param name="myNS" required="yes" />

    <!-- this will cover most things, including a Device that doesn't have a
         sibling Device_Tmp -->
    <xsl:template match="*">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:apply-templates />
      </xsl:element>
    </xsl:template>

    <!-- delete Device_Tmp -->
    <xsl:template match="*:Device_Tmp" />

    <!-- special case for Device elements that have a non-empty sibling
         Device_Tmp -->
    <xsl:template match="*:Device[string-length(../*:Device_Tmp) > 0]"
                  priority="2">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:value-of select="../*:Device_Tmp" />
      </xsl:element>
    </xsl:template>

    <!-- add debugging element to bug -->
    <xsl:template match="*:bug">
      <xsl:element name="tdns:{local-name()}" namespace="{$myNS}">
        <xsl:apply-templates />
        <xsl:element name="tdns:ShowParamValue" namespace="{$myNS}">
          <xsl:value-of select="$myNS" />
        </xsl:element>
      </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Upvotes: 2

wensveen
wensveen

Reputation: 884

It looks like the input nodes aren't in the http://my.com namespace. You can see this because name(.)='bugs' appears to return true. The name() function returns an expanded QName, which should include the namespace (as opposed to local-name()).

I'm not sure why this should be the case, but try using a prefix in your input xml as well.

Upvotes: 0

Related Questions