I have a question about using SQL Server FOR XML to create XML documents with namespaces. I need to create a document like below where there is a namespace definition in the root element (xmlns:ijk="h:/i.j.k"
), and a namespace reference in a child element attribute (ijk:LMNO="3333"
).
I am generating the XML to POST to a Web service:
<ROOT xmlns:ijk="h:/i.j.k" RSTU="1111">
<CHILD CDEF="2222" ijk:LMNO="3333" />
</ROOT>
I wrote this first sample query to get the structure correct before I started fighting with namespaces, and the result is what is expected.
SELECT
[RSTU] = 1111,
(SELECT
[CDEF] = 2222, [LMNO] = 3333
FOR XML RAW('CHILD'), TYPE)
FOR XML RAW('ROOT'), TYPE;
Result
<ROOT RSTU="1111">
<CHILD CDEF="2222" LMNO="3333" />
</ROOT>
Then I tried using WITH NAMESPACES
to add the namespaces, but SQL Server gets carried away and adds all namespaces to all elements. The target Web service does not accept the XML with the namespace overload (in my real case, there are four levels of elements and three namespaces, and it makes a real mess).
WITH XMLNAMESPACES ('h:/i.j.k' as ijk, 'h:/x.y.z' as xyz)
SELECT
[RSTU] = 1111,
(SELECT
[CDEF] = 2222, [ijk:LMNO] = 3333
FOR XML RAW('CHILD'), TYPE)
FOR XML RAW('ROOT'), TYPE;
Result:
<ROOT xmlns:xyz="h:/x.y.z" xmlns:ijk="h:/i.j.k" RSTU="1111">
<CHILD xmlns:xyz="h:/x.y.z" xmlns:ijk="h:/i.j.k" CDEF="2222" ijk:LMNO="3333" />
</ROOT>
I read in Books Online that, while not recommended, namespaces can be added like a regular attribute. I tried this, and it generated the proper namespace definitions:
SELECT
[xmlns:ijk] = 'h:/i.j.k',
[RSTU] = 1111,
(SELECT
[CDEF] = 2222, [LMNO] = 3333
FOR XML RAW('CHILD'), TYPE)
FOR XML RAW('ROOT'), TYPE;
Result:
<ROOT xmlns:ijk="h:/i.j.k" RSTU="1111">
<CHILD CDEF="2222" LMNO="3333" />
</ROOT>
The output above has a good looking namespace definition, but the LMNO attribute does not have its required ijk: namespace reference prefix. I tried adding the namespace reference, but I got an error:
SELECT
[xmlns:ijk] = 'h:/i.j.k',
[RSTU] = 1111,
(SELECT
[CDEF] = 2222, [ijk:LMNO] = 3333
FOR XML RAW('CHILD'), TYPE)
FOR XML RAW('ROOT'), TYPE;
Result:
Msg 6846, Level 16, State 2, Line 34
XML name space prefix 'ijk' declaration is missing for FOR XML column name 'ijk:LMNO'.
Is it possible to write a T-SQL FOR XML query that generates XML where:
a namespace is defined only in the root element, and
the root element has data attributes other than namespace definitions, and
references to the namespace are used in attribute names in child elements?
I reviewed How do I remove redundant namespace in nested query when using FOR XML PATH. In this topic the root element has only namespace definitions and no data attributes.
With your result
It seems like you have an extraneous
xyz
namespace, but the rest seems valid. Defining a namespace isn't the same as applying a namespace to an element or attribute, and defined namespaces can be ignored if they aren't applied (i.e. prepending as on yourLMNO
element). Defining the namespace twice is redundant but shouldn't be invalid.XML is a finicky standard so perhaps the probably is really on the validation which 'requires' this.
Doesn't change your problem, of course, but in the same way that validators have strange expectations, many XML generators don't give this type of flexibility over optional values. They often have the 'this is just what you get' approach.
This is ugly, but a workaround
The result
By using the
RAW
mode for the outerSELECT
it is allowed to place namespace declarations just as if they were attributes.The internal
FOR XML PATH
will not use these namespaces (other behavior withWITH XMLNAMESPACES
!), but it is not possible to use a namespace prefix there.So I add something to the attributes name, cast the whole XML to
NVARCHAR(MAX)
, replace my dummy and cast it back.Please go to the connect issue and vote. This is really annoying!
The repeated namespaces (when using sub-selects) are not wrong but bloating the output and can clash in validators.