slider
slider

Reputation: 431

Filter namespaces and replace their prefixes with XSLT

How do i keep only the required namespaces URIs (maybe unused) and replace all their prefixes (based on list of prefixes and URIs) with XSLT (1.0)? The original combinations of prefixes and namespaces URIs may change!

Input:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:node_0 
xmlns:ns6="http://a.com" 
xmlns:ns5="http://b.com" 
xmlns:ns8="http://c.com" 
xmlns:ns7="http://d.com" 
xmlns:ns2="http://e.com"
xmlns:ns4="http://f.com"
xmlns:ns3="http://g.com">
    <node_1 attr_1="attr_1_val" attr_2="attr_2_val">
        <ns4:node_11>node_11_val</ns4:node_11>
        <ns4:node_12 attr_1="attr_1_val" attr_2="attr_2_val">
            <ns2:node_121>node_121_val</ns2:node_121>
            <ns4:node_122>
                <node_1221>node_1221_val</node_1221>
                <ns3:node_1222>
                    <node_12221 attr_1="attr_1_val">
                        <node_122211>node_122211_val</node_122211>
                    </node_12221>
                </ns3:node_1222>
            </ns4:node_122>
            <node_123>
                <node_1231 attr_1="attr_1_val">
                    <node_12311>node_12311_val</node_12311>
                </node_1231>            
            </node_123>
        </ns4:node_12>
        <ns3:node_13>
            <node_131>
                <ns3:node_1311>node_1311_val</ns3:node_1311>
                <node_1312 attr_1="attr_1_val" attr_2="attr_2_val" attr_3="attr_3_val" attr_4="attr_4_val">
                    <ns3:node_13121/>
                </node_1312>
            </node_131>
        </ns3:node_13>
        <node_14/>
    </node_1>
    <ns4:node_2 attr_1="attr_1_val" attr_2="attr_2_val" attr_3="attr_3_val">
        <ns4:node_21>node_21_val</ns4:node_21>
    </ns4:node_2>
</ns4:node_0>

Required namespaces URIs and their prefixes:

b_ns="http://b.com" 
e_ns="http://e.com"
f_ns="http://f.com"
g_ns="http://g.com"

So the required output:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<f_ns:node_0 
xmlns:b_ns="http://b.com" 
xmlns:e_ns="http://e.com"
xmlns:f_ns="http://f.com"
xmlns:g_ns="http://g.com">
    <node_1 attr_1="attr_1_val" attr_2="attr_2_val">
        <f_ns:node_11>node_11_val</f_ns:node_11>
        <f_ns:node_12 attr_1="attr_1_val" attr_2="attr_2_val">
            <e_ns:node_121>node_121_val</e_ns:node_121>
            <f_ns:node_122>
                <node_1221>node_1221_val</node_1221>
                <g_ns:node_1222>
                    <node_12221 attr_1="attr_1_val">
                        <node_122211>node_122211_val</node_122211>
                    </node_12221>
                </g_ns:node_1222>
            </f_ns:node_122>
            <node_123>
                <node_1231 attr_1="attr_1_val">
                    <node_12311>node_12311_val</node_12311>
                </node_1231>            
            </node_123>
        </f_ns:node_12>
        <g_ns:node_13>
            <node_131>
                <g_ns:node_1311>node_1311_val</g_ns:node_1311>
                <node_1312 attr_1="attr_1_val" attr_2="attr_2_val" attr_3="attr_3_val" attr_4="attr_4_val">
                    <g_ns:node_13121/>
                </node_1312>
            </node_131>
        </g_ns:node_13>
        <node_14/>
    </node_1>
    <f_ns:node_2 attr_1="attr_1_val" attr_2="attr_2_val" attr_3="attr_3_val">
        <f_ns:node_21>node_21_val</f_ns:node_21>
    </f_ns:node_2>
</f_ns:node_0>

There is a XSLT code only for filtering namespaces URIs:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">            
           <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
            <xsl:strip-space elements="*"/>
            <xsl:template match="*">
                <xsl:element name="{name()}" namespace="{namespace-uri()}">
                    <xsl:copy-of select="namespace::*[
                     .='http://b.com'
                     or .='http://e.com'
                     or .='http://f.com'
                     or .='http://g.com"/>
                    <xsl:copy-of select="@*"/>
                    <xsl:apply-templates/>
                </xsl:element>
            </xsl:template>
</xsl:stylesheet>

It's very difficult to write complete code, as there is very little experience with XSLT.
Thanx in advance!

Upvotes: 1

Views: 325

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117018

You have two separate tasks here:

  1. Replace the prefixes of namespaces used by elements of the given XML;

  2. Add the declarations of the unused namespaces to the root element.

The first task is quite simple, since the prefixes are by definition not significant. All you need to do is declare the namespaces using the prefixes you want, then rename the elements in the corresponding namespace using the new prefix.

The second task can be simple if - as you said in the comment - you know the namespace URI of the root element. Then you can copy the namespace declarations from the stylesheet to the elements in that namespace (and hope that the processor is smart enough to do so only once):

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:b_ns="http://b.com" 
xmlns:e_ns="http://e.com"
xmlns:f_ns="http://f.com"
xmlns:g_ns="http://g.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="b_ns:*">
    <xsl:element name="b_ns:{local-name()}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="e_ns:*">
    <xsl:element name="e_ns:{local-name()}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<!-- this matches the root element -->
<xsl:template match="f_ns:*">
    <xsl:element name="f_ns:{local-name()}">
        <xsl:copy-of select="document('')/xsl:stylesheet/namespace::*[not(name()='xsl')]"/>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="g_ns:*">
    <xsl:element name="g_ns:{local-name()}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="*">
    <xsl:element name="{name()}" namespace="{namespace-uri()}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Note:

None of this should be necessary. As noted above, namespace prefixes have no significance. And unused namespaces declarations are redundant. Apparently, this "problem" is caused by a non-conforming parser at the receiving end - and it would be much more productive to fix that.

Upvotes: 2

Related Questions