SQL Server Xml query with multiple namespaces
I have a table in SQL server that contains an Xml column and I am having trouble querying it. I don't know enough about XPath to determine if my query is wrong, or if it is because of what seems like conflicting namespaces. Here is an example xml:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<!-- snip -->
</s:Header>
<s:Body>
<FetchRequest xmlns="http://www.foobar.org/my/schema">
<Contract xmlns:a="http://www.foobar.org/2014/04/datacontracts"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:RequestedBy>John Doe</a:RequestedBy>
<a:TransactionId>ABC20140402000201</a:TransactionId>
</Contract>
</FetchRequest>
</s:Body>
</s:Envelope>
I want to retrieve TransactionId from the xml. The query I tried was this:
SELECT TOP 100
MessageXml,
MessageXml.value('
declare namespace s="http://www.w3.org/2003/05/soap-envelope";
declare namespace a="http://www.w3.org/2005/08/addressing";
(/s:Envelope/s:Body/FetchRequest/Contract/a:TransactionId)[1]', 'varchar(max)')
FROM dbo.Message
I am getting back NULL for my MessageXml.value. If I remove everything after s:Body I seem to get a bunch of text that is concatenated, but as soon as I add FetchRequest I get NULL back in my results.
I did notice that the Contract element defines a namespace of a, and the Envelope also defines a namespace of a, but I wasn't sure if that is a problem or not.
How can I retrieve TransactionId using an XPath query given the above xml example?
I know that answer is accepted, but there is actually simplier way of doing it, if the only thing you need to do is select node value. Just use *
as namespace name:
SELECT MessageXml
, MessageXml.value('(/*:Envelope/*:Body/*:FetchRequest/*:Contract/*:TransactionId)[1]'
, 'varchar(max)')
FROM dbo.Message
You have two problems :
- you're not respecting the implicit default XML namespace on the
<FetchRequest>
node - the XML namespace with the
a:
prefix is first defined on the<s:Envelope>
node, and is being re-declared on the<Contract>
node (really really bad practice in my opinion) and you need to use the second declaration for anything below the<Contract>
node.
So you need something like this (I prefer to define the XML namespaces upfront, in a WITH XMLNAMESPACES()
statement):
;WITH XMLNAMESPACES('http://www.w3.org/2003/05/soap-envelope' AS s,
'http://www.foobar.org/2014/04/datacontracts' AS a,
'http://www.foobar.org/my/schema' AS fb)
SELECT
MessageXml,
MessageXml.value('(/s:Envelope/s:Body/fb:FetchRequest/fb:Contract/a:TransactionId)[1]', 'varchar(max)')
FROM
dbo.Message
This will output the whole query and the value ABC20140402000201
for your second column.