This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 5857 - [XSLT 2.0] A problem with namespace inheritance
Summary: [XSLT 2.0] A problem with namespace inheritance
Status: CLOSED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: XSLT 2.0 (show other bugs)
Version: Recommendation
Hardware: PC Windows NT
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-07-11 19:31 UTC by Michael Kay
Modified: 2012-09-07 08:14 UTC (History)
1 user (show)

See Also:


Attachments

Description Michael Kay 2008-07-11 19:31:40 UTC
We all know that this is an identity template, don't we:

<xsl:template match="*">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>

Well, a Saxon user [see [1]] has just discovered that it isn't. Consider the following input 

<s:complexType xmlns:s="http://s.com/" xmlns="http://t.com/">
  <s:element ref="abcd" xmlns=""/>
</s:complexType>

The result of applying the transform is:

<s:complexType xmlns:s="http://www.w3.com/schema" xmlns="http://t.com/">
  <s:element ref="abcd"/>
</s:complexType>

which loses the xmlns="" undeclaration, and (because the source document is actually a schema and @ref is a QName) causes @ref to be a different QName from the one intended.

How is this happening? We have a bottom-up construction model. <xsl:copy> applied to the <s:element> creates an element with two namespace nodes (for prefixes "s", and "xml"). <xsl:copy> applied to the <s:complexType> creates an element with three namespace nodes (for prefixes "s", "xml", and ""). Then 5.7.1 Constructing Complex Content kicks in, and rule 12 says that these three namespace nodes are inherited by the copied children. So the copied <s:element> acquires a namespace node for ("", "http://t.com"), and therefore no xmlns="" undeclaration appears in the serialized output.

The user can fix this by writing <xsl:copy inherit-namespaces="no">. But should they have to?

Should we perhaps modify the namespace inheritance rule (rule 12) so it does not apply to the default namespace? There's a good reason for making a difference - whereas a 1.1-style undeclaration xmlns:p="" makes p unavailable for use and therefore losing the undeclaration is fairly harmless, the 1.0 undeclaration xmlns="" makes "" refer to a different namespace URI, so losing the undeclaration really matters. 

[1] https://sourceforge.net/forum/message.php?msg_id=5089774
Comment 1 Michael Kay 2008-07-11 19:33:22 UTC
Sorry, I changed the namespace URI for prefix "s" in mid-flight. Hope the example is still comprehensible.
Comment 2 Michael Kay 2009-01-29 16:16:22 UTC
I propose the following fix, implementing the suggestion in comment #0.

In 5.7.1 Constructing Complex Content, rule 12, change the rule from

<quote>
If the newly constructed node is an element node, and if namespaces are inherited, then each namespace node of the newly constructed element (including any produced as a result of the namespace fixup process) is copied to each descendant element of the newly constructed element, unless that element or an intermediate element already has a namespace node with the same name (or absence of a name) or that descendant element or an intermediate element is in no namespace and the namespace node has no name.
</quote>

to

<quote>
If the newly constructed node is an element node, then each inheritable namespace node of the newly constructed element (including any produced as a result of the namespace fixup process) is copied to each descendant element of the newly constructed element, unless that element or an intermediate element already has a namespace node with the same name (or absence of a name) or that descendant element or an intermediate element is in no namespace and the namespace node has no name.

A namespace node is inheritable if both of the following conditions hold:

(a) the namespace node is a binding for a non-default namespace

(b) the [xsl-]inherit-namespaces attribute of the containing instruction has the effective value "yes".
</quote>

I think this will fix the problem, but I think it would be a good idea to test it before making a final decision. This isn't easy to do in Saxon because the actual logic implemented in Saxon bears little resemblence to the way it is described in the spec; but I will do my best.
Comment 3 Michael Kay 2009-01-30 09:26:42 UTC
The WG discussed this on 2009-01-29, and asked the editor to investigate the proposed fix further to make sure there were no unwanted side effects, for example, by doing a trial implementation.

Meanwhile, a couple of further thoughts.

Firstly, I think the problem in the spec is actually worse than the example suggests. If you write:

<xsl:template match="/">
  <s:complexType xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns="http://t.com/">
    <s:element ref="abcd" xmlns=""/>
  </s:complexType>
</xsl:template>

then Saxon outputs (I believe correctly):

<?xml version="1.0" encoding="UTF-8"?>
<s:complexType xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns="http://t.com/">
   <s:element ref="abcd"/>
</s:complexType>

so the xmlns="" is lost even in this simple case of a stylesheet consisting entirely of literal result elements.

Secondly, I think the change needed to the text can be done more simply than proposed in comment #2, by deleting the text between <delete>...</delete> below:

<quote>
If the newly constructed node is an element node, and if namespaces are
inherited, then each namespace node of the newly constructed element (including
any produced as a result of the namespace fixup process) is copied to each
descendant element of the newly constructed element, unless that element or an
intermediate element already has a namespace node with the same name <delete>(or
absence of a name)</delete> or <delete>that descendant element or an intermediate element is in no namespace and</delete> the namespace node has no name.
</quote>

The fact that the fix simplifies the rules rather than complicating them must be encouraging. An alternative formulation is:

<quote>
If the newly constructed node is an element node, and if namespaces are
inherited, then each named (that is, non-default) namespace node of the newly constructed element (including any produced as a result of the namespace fixup process) is copied to each descendant element of the newly constructed element, unless that element or an intermediate element already has a namespace node with the same name.
</quote>

I've also looked a little bit at XQuery to see if the same problem exists there. The answer is that it's slightly different, because of the way copy-namespaces applies to enclosed expressions. I don't think this sheds any useful light (in fact, it only muddies the waters), so I'll pursue the analogous XQuery questions in a separate forum.

Comment 4 Michael Kay 2009-02-02 15:03:54 UTC
I've raised the equivalent XQuery bug as bug #6517.

I've investigated the proposed fix by implementing it in Saxon, and as far as I can tell it appears to work without unwelcome side-effects. As I mentioned earlier, Saxon's internal implementation is very different from the model described in the spec, because Saxon represents namespaces internally using declarations and undeclarations rather than using namespace nodes/bindings. So it's hard to be 100% sure that the changes I made to Saxon to verify this are an accurate reflection of the proposed change. But it feels OK.

The Saxon implementation of the change is essentially that for literal result elements, xsl:copy and xsl:copy-of, an element with no in-scope default namespace is treated as if it had an in-scope namespace binding with (prefix="", uri=""). Some paths checking for errors in xsl:namespace need to be aware of this and allow for it, but otherwise the rest falls out naturally.
Comment 5 Michael Kay 2009-02-02 15:27:04 UTC
I have committed test cases nspc72 to 76 to test for various flavours of this problem. The test results published are assuming the problem is fixed in the way proposed.
Comment 6 Michael Kay 2009-11-13 15:38:00 UTC
At its telcon on 12 Nov 2009 the WG accepted the resolution given by the second version of proposed text in comment #3, namely:

<quote>
If the newly constructed node is an element node, and if namespaces are
inherited, then each named (that is, non-default) namespace node of the newly
constructed element (including any produced as a result of the namespace fixup
process) is copied to each descendant element of the newly constructed element,
unless that element or an intermediate element already has a namespace node
with the same name.
</quote>

This will be the subject of erratum XT.E37 (drafted)

Comment 7 Michael Kay 2011-04-05 10:15:19 UTC
I believe that the fix to this bug also affects the results of tests nspc-n138 and nspc-n139. I've recorded this in test suite bug 931 (member-only).

Also taking the opportunity to mark this as closed.
Comment 8 Michael Kay 2011-06-02 17:00:22 UTC
Tim Mills has identified that the impact of this change is larger than originally thought - see member-only bugzilla #937. I'm therefore re-opening the bug.

I'm not at all sure how to suggest a way forward, however. I think the effect that we would like to achieve is that when a literal result element in the stylesheet explicitly says xmlns="", then the corresponding element in the result tree will have no namespace binding for the default namespace. But it's hard to achieve this without generating an unwanted xmlns="" in cases where the stylesheet didn't explicitly ask for it. The only way I can think of doing this is to represent namespace undeclarations as concrete objects in the data model, which is horribly disruptive.
Comment 9 Michael Kay 2011-06-07 16:10:27 UTC
Note that bug #12738 raises the possibility of a dynamic namespace constructor (in XSLT, an xsl:namespace instruction) creating a namespace node whose namespace URI part is absent, to signify an intent to unbind a namespace that would otherwise be created through the namespace inheritance mechanism. Such a mechanism could be a useful way out of this problem. Specifically, if you have a particular need to generate xmlns="" on an element, for reasons like the one described, then you would be able to do it using <xsl:namespace/>.
Comment 10 Michael Kay 2012-06-21 17:19:04 UTC
The WG decided today to cancel erratum XT.E37 (which was never issued). This leaves the XSLT 2.0 specification unchanged. 

The proposed change was never applied to the draft XSLT 3.0 specification, so no change is needed here.

The relevant test cases need to be rolled back.
Comment 11 Michael Kay 2012-06-26 12:03:29 UTC
Changed the expected results of tests nspc-72 to -76, bug04, and nspc-n138, and -9 to reflect the decision to make no change (i.e. to cancel the proposed Erratum to the spec).
Comment 12 Tim Mills 2012-07-16 08:55:15 UTC
I hate to reopen this again, but I suspect that the expected result for bug04 is now wrong.

The expected result is:

<out xmlns="http://abc.uri/" xmlns:z="http://z.uri/" changed="2004-04-06">
<z:a xmlns="" a-foo="a-bar"></z:a>
<z:b b-foo="b-bar"></z:b>
<c c-foo="c-bar"></c><
/out>

This comes from:

<xsl:variable name="tmp">
<z:a a-foo="a-bar" />
<z:b b-foo="b-bar"/>
<c c-foo="c-bar" />
</xsl:variable>

so I'm at a loss to explain why <z:a> ends up with xmlns="", while <z:b> doesns't.  I suspect that neight <z:a> nor <z:b> should have the xmlns="".
Comment 13 Tim Mills 2012-07-16 10:34:50 UTC
Surely nspc72 is not affected by this bug?
Comment 14 Michael Kay 2012-08-31 14:03:32 UTC
Contrary to what I said in comment #11, It appears I didn't change the expected results of nspc76, and I have now done so.
Comment 15 Michael Kay 2012-09-04 12:26:07 UTC
Fixed bug04 as suggested. (If there are further problems with test cases, please raise them as new test suite bugs.)
Comment 16 Tim Mills 2012-09-07 08:14:46 UTC
bug04 confirmed fixed.