Reputation: 77
I have looked for this specific case, but have not found. I have this XML, that has other elements besides those three, but they are irrelevant. Relevant elements have two namespaces attached to it. I want to remove xmlns:two so in the output only first xmlns is present.
<?xml version="1.0" encoding="UTF-8"?>
<Header>
<one:id xmlns:one="http://x.com/xsd/so"
xmlns:two="http://x.com/xsd/woa.xsd">555</one:id>
<one:protocolVersion xmlns:one="http://x.com/xsd/so"
xmlns:two="http://x.com/xsd/woa.xsd">2.0</one:protocolVersion>
<one:userId xmlns:one="http://x.com/xsd/so"
xmlns:two="http://x.com/xsd/woa.xsd">12345</one:userId>
</Header>
Now what I want, is to remove xmlns:two
<?xml version="1.0" encoding="UTF-8"?>
<Header>
<one:id xmlns:one="http://x.com/xsd/so">555</one:id>
<one:protocolVersion xmlns:one="http://x.com/xsd/so">2.0</one:protocolVersion>
<one:userId xmlns:one="http://x.com/xsd/so">12345</one:userId>
</Header>
I have tried something like this, but it removes wrong namespace. It removes same namespace that is prefix.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:two="http://x.com/xsd/woa.xsd"
xmlns:one="http://x.com/xsd/so">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="one:id">
<xsl:element name="{local-name()}">
<xsl:copy-of select="namespace::*[not(. = namespace-uri(..))]"/>
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Views: 3560
Reputation: 101700
There are lots of things preventing this from creating the result you've described:
Here you're copying only the namespaces that the element does not use rather than the ones that it does:
<xsl:copy-of select="namespace::*[not(. = namespace-uri(..))]"/>
Here you're creating an element in the null namespace (no namespace):
<xsl:element name="{local-name()}">
You've declared the one
and two
namespaces on the XSLT stylesheet's root element, which means they'll be included on the output document's root element.
In order to get the result you've described, you can use this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="one:*" xmlns:one="http://x.com/xsd/so" >
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Or you can use a more general, namespace-agnostic approach:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Both of these produce the output you've said that you want.
Upvotes: 1
Reputation: 70618
The xsl:copy-of
expression you want is this, to copy only the namespace that matches the current element
<xsl:copy-of select="namespace::*[. = namespace-uri(..)]"/>
Additionally, you should be creating the element with name()
and not local-name()
, otherwise you are creating the element in no namespace, given that you have no default namespace.
However, as you are creating a new element with a prefix that has been declared in the XSLT, I don't think you need to copy the namespaces at all.
Try this XSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:one="http://x.com/xsd/so">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="one:*">
<xsl:element name="{name()}">
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Note, if you could use XSLT 2.0, you could also do this....
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:one="http://x.com/xsd/so">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="one:*">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1