Reputation: 51
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) > 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
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
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