Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best solution dealing with default namespaces in XML and XSLT

If you have a default namespace in your input XML and in the XSLT transformation to be done you only want to add one element within that namespace, how would you all do this?

Do you declare the namespace also as a default namespace into the XSLT? Or do you use a prefix in the XSLT and within the XSLT you put all the elements into that namespace?

I created 5 options, from where one option is wrong, and all other 4 give my the correct result, but I just wanted to know which way is to go best or even if there is a better way?

I am using the next XML as input for all examples:

<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://example.org/uri/">
    <element attr="test">
        <one>This is an example</one>
        <two>Pretty nice</two>
    </element>
</data>

Option 1, 2 & 3

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pref="http://example.org/uri/" exclude-result-prefixes="pref">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="pref:element">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>

        <added>Option 1</added>
        <xsl:element name="added" namespace="http://example.org/uri/">Option 2</xsl:element>
        <added xmlns="http://example.org/uri/">Option 3</added>
    </xsl:template>
</xsl:stylesheet>

Which will produce:

<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://example.org/uri/">
    <element attr="test">
        <one>This is an example</one>
        <two>Pretty nice</two>
    </element>
    <added xmlns="">Option 1</added>
    <added>Option 2</added>
    <added>Option 3</added>
</data>

From the above option 1 will not create a correct output, because no default namespace is declared anywhere. Option 2 and 3 will create the correct output, but if I would add more than one element, the XSLT is not looking nicely, because I need to add the namespace on all elements.

Looking at above you would say: I will add a default namespace into the XSLT, like I do in option 4.

Option 4

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://example.org/uri/" xmlns:pref="http://example.org/uri/" exclude-result-prefixes="pref">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="pref:element">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>

        <added>Option 4</added>
    </xsl:template>
</xsl:stylesheet>

This will produce:

<?xml version="1.0" encoding="UTF-8"?>
<data xmlns="http://example.org/uri/">
    <element attr="test">
        <one>This is an example</one>
        <two>Pretty nice</two>
    </element>
    <added>Option 4</added>
</data>

The XSLT looks better, because I do not have to declare the namespace on each element added to the XML. Don't know why exactly why I should or should not do it like this? Maybe I could run into problems when the input XML could have 2 different default namespaces? So sometimes we would receive it with namespace xmlns="http://example.org/uri/" and sometimes in namespace xmlns="http://example.org/uriSecond/".

Option 5

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pref="http://example.org/uri/" exclude-result-prefixes="pref">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="pref:*">
        <xsl:element name="pref:{local-name()}">
            <xsl:apply-templates select="@*|node()" />
        </xsl:element>
    </xsl:template>

    <xsl:template match="pref:element">
        <xsl:element name="pref:{local-name()}">
            <xsl:apply-templates select="@*|node()" />
        </xsl:element>

        <pref:added>Option 5</pref:added>
    </xsl:template>
</xsl:stylesheet>

Will produce:

<?xml version="1.0" encoding="UTF-8"?>
<pref:data xmlns:pref="http://example.org/uri/">
    <pref:element attr="test">
        <pref:one>This is an example</pref:one>
        <pref:two>Pretty nice</pref:two>
    </pref:element><pref:added>Option 5</pref:added>
</pref:data>

Also a correct result and all elements are rewritten into a prefixed namespace. Also no default namespace is declared into the XSLT. If I now would receive a second input with a different default namespace, I could also declare this and copy all the pref: templates with that prefix.

So I am really searching for the best solution so I won't run into any troubles later on. Or are all working solutions a good way, but depending on the question choose your path?

like image 847
Mark Veenstra Avatar asked Nov 20 '13 08:11

Mark Veenstra


1 Answers

I would consider option 4 to be clearest.

If you have to add nodes in different default namespaces in different templates then you can go for something in between 3 and 4 and put an xmlns="..." on each template:

<xsl:template match="ns1:something" xmlns="urn:ns1">
  <element1 />
  <element2 />
</xsl:template>

But your last paragraph sums it up nicely - learn all the options and use whatever makes most sense in each situation.

like image 95
Ian Roberts Avatar answered Sep 27 '22 18:09

Ian Roberts