3

I encountered a peculiar difference in xslt behavior when the root element has a default namespace attribute as opposed to when it does not.
I am wondering why this difference occurs.

The XML input is

<root>
    <content>xxx</content>
</root>

When the following transformation is applied

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

    <xsl:template match="/">
        <root>
            <xsl:apply-templates/>
        </root>
    </xsl:template>

    <xsl:template match="content">
        <w>x</w>
    </xsl:template>

</xsl:stylesheet>

the result is the expected

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <w>x</w>
</root>

But when the same transformation is applied to

<root xmlns="http://test.com">
    <content>xxx</content>
</root>

the result is different and based on the application of just default templates (which effectively output the text node value 'xxx'):

<?xml version="1.0" encoding="UTF-8"?>
<root>xxx</root>

Addition

When this is the expected behavior in this case, then what match attribute value is needed to match the content element in the second case?

Maestro13
  • 3,656
  • 8
  • 42
  • 72

2 Answers2

5

This is the most FAQ in XPath / XSLT.

An unprefixed element name is treated by XPath as belonging to "no namespace".

The W3C Xpath specification says:

if the QName does not have a prefix, then the namespace URI is null.

Therefore, in a document with a default namespace a refernce to an element with unprefixed name (say "someName") selects nothing, because there isn't any element in "no namespace" in the XML document, but someName means an element with name "someName", belonging to "no namespace".

The Solution:

If we want to select an element by name, we must prefix that name and the prefix must be associated with the default namespace.

This transformation:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:x="http://test.com" exclude-result-prefixes="x">
        <xsl:output omit-xml-declaration="yes" indent="yes"/>
        <xsl:strip-space elements="*"/>

        <xsl:template match="/">
            <root>
                <xsl:apply-templates/>
            </root>
        </xsl:template>

        <xsl:template match="x:content">
            <w>x</w>
        </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document with default namespace:

<root xmlns="http://test.com">
    <content>xxx</content>
</root>

produces the wanted, correct result:

<root>
   <w>x</w>
</root>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Yes - should have known that a default namespace changes things - I guess I just thought that only non-default ones would necessitate prefixing the element name (or using `local-name()`). – Maestro13 Apr 14 '12 at 21:31
  • @Maestro13: This is a rule to remember: If a default namespace is used, then to select a particular element, whose name is unprefixed in the XML document, we must prefix it in the XPath expression. I have edited my answer and now, in addition to the complete transformation, I provide the exact quote from the W3C XPath specification, and a link to the section containing that text. – Dimitre Novatchev Apr 14 '12 at 21:34
  • I now remember this one coming by a month ago: [namespace-stopping-xslt-working](http://stackoverflow.com/questions/9886520/namespace-stopping-xslt-working) - finally it all makes sense to me now! – Maestro13 Apr 14 '12 at 23:01
  • @Maestro13: Yes, there are many questions like this -- both in the "xslt" and in the "xpath tags -- almost everyday someone asks yet another question about selection with default namespace ... – Dimitre Novatchev Apr 15 '12 at 06:22
2

so what exactly is your question? If you're simply looking for an explanation, following is a brief one. What you're observing is the proper behavior according to the specification. When you put a namespace on something, the parser essentially treats it as a different element entirely (than an element of the same name but no namespace). Therefore, in the second situation, when you say <xsl:template match="content">, it doesn't match the <content> element in the XML file because it falls under the http://test.com namespace (by way of the namespace declaration on its parent). Therefore, the default templates take over.

user1263226
  • 250
  • 3
  • 12
  • 1
    OK sounds clear although a little unexpected - an additional question then: how match the `content` element in the second case? – Maestro13 Apr 14 '12 at 21:09
  • 1
    fyi there are a couple ways to match the element with the namespace. One way would be to add it to your XSL declaration, i.e. ``, in which case you would have to change your template match statement to ``. Or, you can simply change your template match statement to `` – user1263226 Apr 14 '12 at 21:12
  • Oops forgot the quotes, in my last sentence the word content should be surrounded by apostrophes, i.e. 'content' – user1263226 Apr 14 '12 at 21:17
  • Clear again - the second alternative was one I thought of already, but looked a little overdone to me - I should have mentioned that, scusi. Thank you for your answer! – Maestro13 Apr 14 '12 at 21:18
  • 1
    @Maestro13 - If you're using XSLT 2.0, you can also use `*` as the prefix: `match="*:content"` – Daniel Haley Apr 16 '12 at 14:32