I am trying to use an XPath expression to select nodes or node-sets in an XML document inside a SQL Server stored procedure.
I am trying to do something similar to code in c#
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(response.GetResponseStream());
XmlNode contact = xmlDoc.SelectSingleNode("//users/user/contact");
string strContact = contact.InnerText.Trim();
The XML is a result of call to Web Service from within a stored procedure.
Similar to this example.
Calling Web Service from stored procedure
However the XML looks like this:
<DocumentElement xmlns="">
<stats>
<delivered>5</delivered>
</stats>
</DocumentElement>
I need to retrieve the value of the node delivered using a statement similar to
XmlNode delivered = xmlDoc.SelectSingleNode("//stats/delivered");
You're obviously ignoring the XML namespace that's defined on your root element - you need to respect that XML namespace and include it in your query!
-- Sample data - using a *SAMPLE* namespace - replace with your own!
DECLARE #input XML = '<DocumentElement xmlns="urn:sample-namespace">
<stats>
<delivered>5</delivered>
</stats>
</DocumentElement>'
-- set up a query that *uses* that XML namespace instead of ignoring it...
;WITH XMLNAMESPACES( DEFAULT 'urn:sample-namespace')
SELECT
#input.value('(/DocumentElement/stats/delivered)[1]', 'int')
Related
I am trying to modify the attribute of an XML root attribute in XQuery with T-SQL but I don't manage to do that. My XML has a namespace in it and I can't exactly bypass this. When I query the value of the xml I successfully retrieve the value because I use: ;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1'). I also tried to use: 'declare default element namespace "some:namespace:here:v1";' in the XQuery but does not seem to work.
Any ideas of how can I achieve this ?
This is an example of the XML I am trying to modify.
DECLARE #XML_TO_READ XML = N'
<F2101 xmlns="some:namespace:here:v1" propertyToModify="valueToModify">
<person xmlns="some:namespace:here:v1" anotherPropertyToModify="anotherValueToModify" />
</F2101>'
I retrieve the value like this:
;WITH XMLNAMESPACES (DEFAULT 'some:namespace:here:v1')
SELECT propertyToModify =
#XML_TO_READ.value('(/F2101/#propertyToModify)[1]', 'nvarchar(50)')
And I tried to modify (update) the value like this:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/propertyToModify/text())[1] with ("modifiedValue")')
I tried multiple solutions but I did not find anything that would work for my special case here.
Thanks in advance.
Your statement should be:
SET #XML_TO_READ.modify('
declare default element namespace "some:namespace:here:v1";
replace value of (/F2101/#propertyToModify)[1] with ("modifiedValue")')
Note the "#propertyToModify" rather than "propertyToModify/text()".
Also, documentation link: replace value of (XML DML).
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.
FetchRequest and Contract are in namespace http://www.foobar.org/my/schema and alias a is redefined in the document. You need:
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";
declare namespace f="http://www.foobar.org/my/schema";
declare namespace x="http://www.foobar.org/2014/04/datacontracts";
(/s:Envelope/s:Body/f:FetchRequest/f:Contract/x:TransactionId)[1]', 'varchar(max)')
FROM dbo.Message;
Fiddle here:
Hello I have the following xml structure within a database table column :
DECLARE #Response XML =
'<star:ShowInfo xmlns="http://www.starstandard.org/STAR/5"
xmlns:ns2="http://www.openapplications.org/oagis/9"
xmlns:star="http://www.starstandard.org/STAR/5"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" releaseID="5.1.5"
xsi:noNamespaceSchemaLocation="">
<ShowDataArea>
<ServiceInfo>
<SVPlanInfo>
<AKStatus>
<Code>Error</Code>
<STText xsi:type="ns2:TextType">E12143 - Please fetch me from this xml </STText>
</AKStatus>
</SVPlanInfo>
</ServiceInfo>
</ShowDataArea></star:ShowInfo>'
In the above xml I need to fetch the STText value which is
E12143 - Please fetch me from this xml . Can anyone point me on how I can do it ?
I tried the following but it doesnt seem to work :
;WITH XMLNAMESPACES ('http://www.w3.org/2001/XMLSchema' as xsd,
'http://www.w3.org/2001/XMLSchema-instance' as xsi)
SELECT #Response.value('(/xsd:Response)[1]','nvarchar(500)') as ExceptionMessage
What a pain.
remove:
xmlns="http://www.starstandard.org/STAR/5"
It is not a sql issue, but rather namespace-getting-confused issue.
Its heplful totake SQL out the equation sometimes, by testing some place like
http://xpath.online-toolz.com/tools/xpath-editor.php.
I encountered a strange behavior on SQL Server 2008 R2 when performing a Xpath query on a XML field with nodes which have an empty namespace.
This query doesn't return results:
[xml_field].query('/RootNode/NodeWithEmptyNamespace')
This query returns results:
[xml_field].query('/dft:RootNode/NodeWithEmptyNamespace')
For clarification this query also returns results so no prefix is mandatory for root node (and probably default namespace):
[xml_field].query('/RootNode')
According to the XML namespace default documation when empty namespace is defined the namespace is none.
The XML in the database is the following:
<RootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org">
<otherNode>Dummy data</otherNode>
<NodeWithEmptyNamespace xmlns="">Other dummy data</NodeWithEmptyNamespace>
</RootNode>
The full query:
WITH XMLNAMESPACES ('http://tempuri.org' as dft)
SELECT TOP 150 [ID],
[xml_field].query('/dft:RootNode/NodeWithEmptyNamespace')
FROM [database];
Does anyone have an explanation for this behavior or is this a bug?
It is not clear what your problem is. With the XML sample you have posted the RootNode element and the otherNode element are in the namespace http://tempuri.org while the NodeWithEmptyNamespace element is in no namespace (as the xmlns="" puts it there). With XPath a path or step NodeWithEmptyNamespace selects elements of that name in no namespace and RootNode would select an element of that name in no namespace too, only in your input there is no such element. So your path /dft:RootNode/NodeWithEmptyNamespace is doing the right thing, it selects the element with local name RootNode in the namespace tied to the dft prefix (i.e. http://tempuri.org) and its child element with local name NodeWithEmptyNamespace in no namespace.
I'm trying to get some values out of an Xml Datatype. The data looks like:
<Individual xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Lirria</FirstName>
<LastName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Latimore</LastName>
</Indvidual>
Note the presence of the xmlns in the elements FirstName and LastName - this is added when we create the xml by serializing a c# business object. Anyway it seems that the presence of this namespace in the elements is causing XQuery expressions to fail, such as:
SELECT MyTable.value('(//Individual/LastName)[1]','nvarchar(100)') AS FirstName
This returns null. But when I strip out the namespace from the elements in the xml (e.g. using a Replace T-SQL statement), the above returns a value. However there must be a better way - is there a way of making this query work i.e. without updating the xml first?
Thanks
John Davies
You need to properly name the element you want to select. See Adding Namespaces Using WITH XMLNAMESPACES. Here is an example using your XML:
declare #x xml;
set #x = N'<Individual
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FirstName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Lirria</FirstName>
<LastName xmlns="http://nswcc.org.au/BusinessEntities.Crm">Latimore</LastName>
</Individual>';
with xmlnamespaces (N'http://nswcc.org.au/BusinessEntities.Crm' as crm)
select #x.value(N'(//Individual/crm:LastName)[1]',N'nvarchar(100)') AS FirstName
The * wildcard will also allow you to select the element without enforcing the explicit namespace. Remus' answer is the way to go, but this may assist others having namespace issues:
select #x.value(N'(//Individual/*:LastName)[1]',N'nvarchar(100)')