I am trying to replace namespace string using xslt.
I have the source namespace string and target namespace string in another xml file in the format of "namespace source="xxx" target="xxx"". All source namespace strings in my to-be-transformed xml should be changed to the corresponding target value. Only elements need to be considered as attributes don't have namespace.
I'm using the JDK default xalan xslt processor, which supports xslt 1.0. I'd like to stick to xslt 1.0 if possible, but I can change processor and use xslt 2.0 if really needed.
For example, to-be-transformed input xml:
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
The output xml should be ("http://ns2-old" is changed to "http://ns2-new", and "http://ns4-old" is changed to "http://ns4-new"):
<root xmlns="http://ns1" xmlns:ns2="http://ns2-new" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
Here is my xsl that is not working:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="nsmapdoc" select="document('my-map-file')"/>
<xsl:key name="nsmap" match="//namespace/@target" use="@source"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<!-- process each element -->
<xsl:template match="element()">
<xsl:for-each select="namespace::*">
<!-- for each namespace node of the element, save the string value-->
<xsl:variable name="sourceuri"><xsl:value-of select="."/>
</xsl:variable>
<!-- get the target value for this namespace-->
<xsl:variable name="targeturi">
<xsl:for-each select="$nsmapdoc">
<xsl:value-of select="key('nsmap', $sourceuri)"/>
</xsl:for-each>
</xsl:variable>
<!-- if target value exists, replace the current namespace node string value with the target value-->
<xsl:if test="$targeturi">
<xsl:value-of select="$targeturi"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have a few questions:
For the identity template that do the copy, why doing "match="node()|@*" instead of just "match="node()"? Is attribute also a node?
I am not sure if I did correctly to get the namespace string value (like "http://ns1", "http://ns2-old") for the element.
I think I got the key correctly. However, I am not sure if I used the type correctly---looks like targeturi variable is not a string. If key does not have the entry, what will lookup the entry return? In my case, I should replace the namespace value only for the namespace that has an entry in the map.
How to write a new string value for the namespace node?
I need to process each namespace nodes for the element. Is it the right way to define a variable inside ?
please help to see what is wrong with my xsl. Any suggestion is appreciated.