Search code examples
sqlxmlt-sqlattributesxml-namespaces

XML default namespace and prefixed attributes


I stumbled over some odd behaviour in connection with prefixed attributes. I know - in general - that in most cases prefixed attributes are not used, as they belong to an element which is in a namespace already. But shortly I tried to answer a question and found something I could not understand, even after some research:

DECLARE @xml XML=
N'<test:root xmlns:test="SomeURL">
    <test:SomeElement test:SomeAttribute="yeah!" />
  </test:root>';

I can wildcard the namespace:

SELECT @xml.value(N'(/*:root/*:SomeElement/@*:SomeAttribute)[1]',N'nvarchar(max)');

I can give an alias to the namespace:

WITH XMLNAMESPACES('SomeURL' AS ns)
SELECT @xml.value(N'(/ns:root/ns:SomeElement/@ns:SomeAttribute)[1]',N'nvarchar(max)');

I thought - as there is only one namespace - I could use it as default:

WITH XMLNAMESPACES(DEFAULT 'SomeURL')
SELECT @xml.value(N'(/root/SomeElement/@SomeAttribute)[1]',N'nvarchar(max)'); --fails!

If I use the same as above but set a wildcard to the attribute it works:

WITH XMLNAMESPACES(DEFAULT 'SomeURL')
SELECT @xml.value(N'(/root/SomeElement/@*:SomeAttribute)[1]',N'nvarchar(max)');

When I use old fashioned FROM OPENXML I see, that the attribute is a member of test / SomeURL namespace, just as all elements:

DECLARE @hdoc INT;
EXEC sp_xml_preparedocument @hdoc OUTPUT, @xml;

SELECT * FROM OPENXML (@hdoc, '//*',3);  

EXEC sp_xml_removedocument @hdoc;

The result:

+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| id | parentid | nodetype | localname     | prefix | namespaceuri | datatype | prev | text    |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 0  | NULL     | 1        | root          | test   | SomeURL      | NULL     | NULL | NULL    |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 2  | 0        | 2        | test          | xmlns  | NULL         | NULL     | NULL | NULL    |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 5  | 2        | 3        | #text         | NULL   | NULL         | NULL     | NULL | SomeURL |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 3  | 0        | 1        | SomeElement   | test   | SomeURL      | NULL     | NULL | NULL    |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 4  | 3        | 2        | SomeAttribute | test   | SomeURL      | NULL     | NULL | NULL    |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+
| 6  | 4        | 3        | #text         | NULL   | NULL         | NULL     | NULL | yeah!   |
+----+----------+----------+---------------+--------+--------------+----------+------+---------+

This is a tiny issue, as it is easy to find a work around, but I'm curious...

Any light on this?


Solution

  • In XPath, as in XML, the default namespace does not apply to attributes. An element name written without a prefix assumes the default namespace, while an attribute name written without a prefix assumes "no namespace".