Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSLT 2.0 - change namespace without discarding existing prefix bindings

Here's my input XML document:

<test xmlns="http://www.example.com/v1">
  <qnameValue xmlns:foo="http://foo.example.com/">foo:bar</qnameValue>
</test>

I want to use XSLT (2.0) to change the namespace of this document to v2, i.e. the desired output is:

<test xmlns="http://www.example.com/v2">
  <qnameValue xmlns:foo="http://foo.example.com/">foo:bar</qnameValue>
</test>

I'm trying to use this stylesheet:

<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
     xmlns:previous='http://www.example.com/v1'>
  <xsl:output encoding='UTF-8' indent='yes' method='xml'/>
  <!-- Identity transform -->
  <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='http://www.example.com/v2'>
      <xsl:apply-templates select='@* | node()' />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Unfortunately the output turns out as:

<test xmlns="http://www.example.com/v2">
  <qnameValue>foo:bar</qnameValue>
</test>

i.e. the crucial namespace binding on qnameValue has gone. Is there any way to force a copy of all namespace bindings to the output?

like image 608
David North Avatar asked Dec 17 '25 04:12

David North


1 Answers

This should do it, and is XSLT 1.0 compatible:

<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
     xmlns:previous='http://www.example.com/v1'>
  <xsl:output encoding='UTF-8' indent='yes' method='xml'/>
  <!-- Identity transform -->
  <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='http://www.example.com/v2'>
      <xsl:copy-of select='namespace::*[not(. = namespace-uri(current()))]' />
      <xsl:apply-templates select='@* | node()' />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

When run on your sample input, the result is:

<test xmlns="http://www.example.com/v2">
  <qnameValue xmlns:foo="http://foo.example.com/">foo:bar</qnameValue>
</test>

This is a similar approach that might be a small bit more efficient by storing the old uri in a variable and accessing it from there:

<xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
     xmlns:previous='http://www.example.com/v1'>
  <xsl:output encoding='UTF-8' indent='yes' method='xml'/>
  <xsl:variable name='oldUri' select='namespace-uri((//previous:*)[1])' />

  <!-- Identity transform -->
  <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='http://www.example.com/v2'>
      <xsl:copy-of select='namespace::*[not(. = $oldUri)]' />
      <xsl:apply-templates select='@* | node()' />
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>
like image 135
JLRishe Avatar answered Dec 19 '25 21:12

JLRishe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!