0

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>
2
  • 1
    Note that your XML is not well-formed. You have an opening <one:id> tag, but a closing </xro:id> tag. Commented Jun 22, 2018 at 11:00
  • Thanks @TimC, I fixed it. Commented Jun 22, 2018 at 11:07

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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>

1 Comment

In fact you could just unconditionally do <xsl:copy-of select="/" copy-namespaces="no"/> at the top level - this removes all unused namespaces, which might (or might not) meet the requirement. At any rate, it works for this example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.