arzillo
arzillo

Reputation: 183

xsl:template match behaviour with and without namespace

I'm trying to format a PDF report while using apache fop, and I found a behaviour that I cannot understand.

While trying to debug the issue, I tracked it down to the xsl behaviour of xsl:template match directive, with and without namespace declared in xml file.

The behaviour of xsl is the same even if I use another implmentation (xsltproc), so the difference should be in the xsl spec an it's not related to apache fop, but I was not able to find an explanation, so I will appreciate very much if someone can help me.

Here is the code. FIrst the "bugger.xml" file:

<?xml version="1.0" encoding="UTF-8" ?>
<parent xmlns="http://www.bugger.org">parent_value</parent>

And here is the "bugger.xsl" file:

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

<xsl:template match="/">
    OK, root element matches...
    <xsl:apply-templates />
</xsl:template>

<xsl:template match="/*">
    you WILL see this writing... I'm a parent tag, look: I'm a "<xsl:value-of select="name()"/>", why you cannot see me in the /parent template match?
    The naming match fails because of the xmlns directive in the bugger.xml file. If you remove it, all works fine.
</xsl:template>

<xsl:template match="/parent">
    you will NOT see this writing, while you should see it...
</xsl:template>

</xsl:stylesheet>

The question is: why if I place the xmlns="http://www.bugger.org" directive, the xsl:template matches the "/*" and if I remove it, the xsl:template matches the "/parent" ?

Thanks for your help!!!

Upvotes: 0

Views: 3327

Answers (2)

Tony Graham
Tony Graham

Reputation: 8068

With:

<parent xmlns="http://www.bugger.org">parent_value</parent>

you've set the 'default namespace' to the 'http://www.bugger.org' namespace URI. See https://www.w3.org/TR/REC-xml-names/#defaulting

The XSLT processor (and anything else that implements REC-xml-names that processes your XML) sees the 'parent' element as being 'in' (not a technical term) the 'http://www.bugger.org' namespace.

In your stylesheet, /* matches because * (in this context) matches all elements. /parent does not match, because that matches only a parent with a null namespace URI (which is why it matched when you removed the namespace declaration). See https://www.w3.org/TR/xpath/#node-tests

(Also, in the stylesheet that you show, you don't need the leading / in your match patterns.)

To specifically match on a parent with the 'http://www.bugger.org' namespace URI, you need to match on the 'qualified name' (QName) for the element. I.e., you need to use a namespace prefix with parent in your match patterns even though you could use the same namespace URI as the default namespace in your source XML (because, in XSLT, unqualified names in patterns are always in the null namespace):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:b="http://www.bugger.org"
                version="1.0"
                exclude-result-prefixes="b">

<xsl:template match="b:*">
    you will NOT see this writing, because it has a lower priority...
</xsl:template>

<xsl:template match="b:parent">
    you will see this writing.
</xsl:template>

</xsl:stylesheet>

Upvotes: 3

michael.hor257k
michael.hor257k

Reputation: 117175

When your source is:

<?xml version="1.0" encoding="UTF-8" ?>
<parent xmlns="http://www.bugger.org">parent_value</parent>

this template:

<xsl:template match="/parent">
    you will NOT see this writing, while you should see it...
</xsl:template>

does not match anything in your source XML file, and will not be applied. OTOH, this template:

<xsl:template match="/*">
    you WILL see this writing... I'm a parent tag, look: I'm a "<xsl:value-of select="name()"/>", why you cannot see me in the /parent template match?
    The naming match fails because of the xmlns directive in the bugger.xml file. If you remove it, all works fine.
</xsl:template>

does match the root element (whose local name is parent in this example), and therefore will be applied to it.


Now, when your source is:

<?xml version="1.0" encoding="UTF-8" ?>
<parent>parent_value</parent>

then both templates match the root parent element - and with the same priority (of 0.5). In such case, the processor will choose the last one of the matching templates - see: https://www.w3.org/TR/xslt/#conflict


Note:

  1. /is the root node, not the root element.

  2. The "xmlns="http://www.bugger.org" directive" is called a namespace declaration - and you'd be well advised to learn more about namespaces in XML and how to handle them in XSLT.

Upvotes: 2

Related Questions