Parsing XML with namespaces in SQL Server - sql

I am having a hard time trying to parse an XML that has some namespaces defined:
<TravelItineraryReadRS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="2.2.0">
<TravelItinerary xmlns="http://webservices.sabre.com/sabreXML/2011/10">
<CustomerInfo>
<PersonName WithInfant="false" NameNumber="01.01" RPH="1">
<GivenName>JEFF S</GivenName>"
The XMl is stored in an XML type column named response, and I want to get the GivenName value, for which I use the below query:
;WITH XMLNAMESPACES (DEFAULT 'http://webservices.sabre.com/sabreXML/2011/10')
select
response.value('(/TravelItineraryReadRS/TravelItinerary/CustomerInfo/PersonName[1]/GivenName)[1]', 'nvarchar(50)') AS Name
from dbo.RezMonitorXMLdataTest where locator = 'GUBXRV'
but instead of getting JEFF S as a result I get NULL. I think this might be related to the namespaces used. Does anyone know how could I get the GivenName value?
Thanks in advance,
Guzmán

Since your top-level node <TravelItineraryReadRS> is not part of that XML namespace, you cannot use the DEFAULT qualifier. Instead, you have to define a XML namespace prefix, and include that in your XQuery:
;WITH XMLNAMESPACES ('http://webservices.sabre.com/sabreXML/2011/10' AS ns)
SELECT
XmLContent.value('(/TravelItineraryReadRS/ns:TravelItinerary/ns:CustomerInfo/ns:PersonName[1]/ns:GivenName)[1]', 'nvarchar(50)') AS Name
FROM
dbo.RezMonitorXMLdataTest
WHERE
locator = 'GUBXRV'

Related

Select values from XML with multiple namespaces

I need to read a value of an attribute from an XML column. The data is an XML with multiple namespaces declared:
<sd:objectData xmlns:sd="http://sd-uri">
<sd:object sourceKey="FC5A0A51-7FB6-4C64-A13E-D4B00649E80E">
<do:properties xmlns:do="http://do-uri">
<do:property name="DECISION">
<do:propertyValues clearExistingValues="true">
<do:propertyValue action="add" valueInteger="1000142" tag="Approve" />
</do:propertyValues>
</do:property>
</do:properties>
</sd:object>
</sd:objectData>
I want to read the value of valueInteger, namely in this example 1000142. I tried with WITH XMLNAMESPACES() but I am not able to get it together to define both aliases.
Does this work for you?
DECLARE #XML xml = '
<sd:objectData xmlns:sd="http://sd-uri">
<sd:object sourceKey="FC5A0A51-7FB6-4C64-A13E-D4B00649E80E">
<do:properties xmlns:do="http://do-uri">
<do:property name="DECISION">
<do:propertyValues clearExistingValues="true">
<do:propertyValue action="add" valueInteger="1000142" tag="Approve" />
</do:propertyValues>
</do:property>
</do:properties>
</sd:object>
</sd:objectData>';
WITH XMLNAMESPACES ('http://sd-uri' AS sd,
'http://do-uri' AS do)
SELECT #XML.value('(/sd:objectData/sd:object/do:properties/do:property/do:propertyValues/do:propertyValue/#valueInteger)[1]','int') AS valueInteger;
In addition to Larnu's answer (which is the best and correct answer) just some alternative shortcuts, if you just want to get one value:
This query fetches the needed value in four different approaches
SELECT #XML.value(N'(//*/#valueInteger)[1]',N'int') AS Super_easy_with_double_wildcard
,#XML.value(N'(//*:propertyValue/#valueInteger)[1]',N'int') AS Easy_with_namespace_wildcard
,#XML.value(N'declare namespace do="http://do-uri";
(//do:propertyValue/#valueInteger)[1]',N'int') AS with_local_declaration
,#XML.value(N'declare namespace do="http://do-uri";
declare namespace sd="http://sd-uri";
(/sd:objectData/sd:object/do:properties/do:property/do:propertyValues/do:propertyValue/#valueInteger)[1]',N'int') AS with_full_local_declaration;
The general advise is: Be as specific as possible to avoid hassels. If you do no bother and you just need a readable, quick catch, you can take one of the alternatives.
UPDATE Add a predicate
With a predicate you can place a filter:
SELECT #XML.value(N'(//*:property[#name="DECISION"]//*:propertyValue/#valueInteger)[1]',N'int') AS Example_with_predicate

Extract XML nodes using OPENXML

sample_xml I tried to extract XML data using OPENXML in SQL, but the XML file contain prefixes such as: "pidx:CustomerID>01234", see sample_xml.
If I exclude the prefix "pidx:" it can't read data, if I include, error out:
Msg 6603, Level 16, State 2, Line 15 XML parsing error: Reference to
undeclared namespace prefix: 'pidx'.
How do I do it?
Besides the fact, that you should never post your code / XML as a picture, there are some general hints:
Your prefixes are binding an element to a namespace.
This namespace must be declared!
If your xml (as posted in the picture) is complete, it is invalid! If it is just a portion of a bigger XML you'll find the namespace's declaration somewhere above, in most cases in the root element
OPENXML is outdated and should not be used any more. Better use the XML's native methods like .value or .nodes().
update
As requested in a comment some explanation about the need to declare a namespace. This XML is not valid:
<abc:test>1</abc:test>
Try this:
DECLARE #xml XML=N'<abc:test>1</abc:test>';
You'll get this
Msg 9459, Level 16, State 1, Line 1 XML parsing: line 1, character 10,
undeclared prefix
Now declare the namespace and it works
DECLARE #xml XML=N'<abc:test xmlns:abc="blah">1</abc:test>';
Such a namespace is valid for the declaring node and all elements hierarchically below (=within).
In most cases namespaces are declared within a root node
Try this
DECLARE #xml XML=
N'
<root xmlns:abc="blah">
<abc:test>1</abc:test>
</root>
';
SELECT #xml

how to use following xml query with out prefix

In following link
https://msdn.microsoft.com/en-us/library/ms175178.aspx
i found this cote "This prefix is then used in the query body instead of the namespace URI"
This makes me fell that if i do not specify prefix then i have to use uri in place of prefix. So please give me an example of using uri in place of prefix,
because in above link it is not given. only they given example of with prefix.
yours sincerley
This question is not a good question: Please read How to ask a good SQL question and How to create a MCVE
But I'll try to answer it...
A namespace is meant to separate identically named elements. Very often XML fragments are just glued together. It is very likely, that different elements with the same name get together. By using a namespace the combination of namespace and element name is used to identify the element. But it is not important, which alias is used to point to the namespace!
You must distinguish between the "namespace" and the given "alias".
The following example has the element <a> in two flavours. Check this out:
DECLARE #xml XML=
N'<root xmlns="DefaultNS" xmlns:dns="DerivedNS">
<a>This is default</a>
<dns:a>This is derived</dns:a>
</root>';
--no result
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'/root/a') AS A(a);
--wildcard-namespace: Everything
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'/*:root/*:a') AS A(a);
--Only default namespace declared: Only default element returned
WITH XMLNAMESPACES(DEFAULT 'DefaultNS')
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'/root/a') AS A(a);
--.nodes() calls for the derived namespace: Only derived element returned
WITH XMLNAMESPACES(DEFAULT 'DefaultNS'
,'DerivedNS' AS xyz) --Other prefix, doesn't matter
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'/root/xyz:a') AS A(a); --prefix "dns" would not work, the alias is now "xyz"
--Here I use random prefixes. I took "dns" as alias for the default!
WITH XMLNAMESPACES('DefaultNS' AS dns
,'DerivedNS' AS dns2) --random prefixes for the namespaces, took even dns for the wrong one!
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'/dns:root/dns:a') AS A(a); --prefix "dns" returns the default ns now! And you need the prefix at `root` too!
--With inline declaration
SELECT a.value(N'(text())[1]',N'nvarchar(max)')
FROM #xml.nodes(N'declare namespace x="DefaultNS"; /x:root/x:a') AS A(a); --The alias "x" is now bound to the default namespace!

XML parsing with namespace SQL Server

We are cleaning up data in our database and a column has XML details inside of it which we want to be able to convert into plain text.
Below is the sample XML in the table column.
<FlowDocument PagePadding="5,5,5,5" Name="RTDocument" AllowDrop="True" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Paragraph>FILE DESTROYED - MAY 21st, 2008</Paragraph>
<Paragraph>todo</Paragraph>
</FlowDocument>
I am using this query, but it is not rendering the desired output due to the presence of Namespace (if I remove the namespace from the XML, I am able to render the output successfully).
SELECT
CAST(CAST(Comments AS XML).query('data(/FlowDocument/Paragraph)') AS VARCHAR(7000)) AS activity
FROM
dbo.Activities
WHERE
ActivityID = 1
Kindly help in this matter.
Thanks
You can also declare your namespace like this:
;WITH xmlnamespaces(DEFAULT 'http://schemas.microsoft.com/winfx/2006/xaml/presentation')
SELECT
CAST(CAST(Comments AS XML).query('data(/FlowDocument/Paragraph)') AS VARCHAR(7000)) AS activity
FROM [dbo].Activities where ActivityID=1
Other options are given here: parsing xml using sql server
You need to use namespace declaration in your Query as per: https://msdn.microsoft.com/en-us/library/ms191474.aspx
so your query portion would look something like:
query('
declare namespace NS="http://schemas.microsoft.com/winfx/2006/xaml/presentation";
data(/NS:FlowDocument/NS:Paragraph)
')

Parsing XML Element value entirely in SQL form arbitrary string

We have log/audits we have compiled over some time that we would like to run some brief reports on.
One of the columns in the logs is JSON, but contains XML. We want to be able to parse out the value of a certain XML tag for each of the rows. So given an arbitrary string such as the following:
{ "XmlData" :"<tag1><tag2><TagToParse>234</TagToParse></tag2><tag1>".....}
I would like to run a sql query that return 234 when I give it the tag name TagToParse
What is the easiest way to do this ENTIRELY in SQL?
Give your container will always be tag1, then something like this should do it:
DECLARE #MyXML XML
SET #MyXML = '<tag1><tag2><TagToParse>234</TagToParse></tag2></tag1>'
SELECT
a.b.value('(/tag1//TagToParse/node())[1]', 'nvarchar(max)') AS Tag
FROM #MyXML.nodes('tag1') a(b)
Good luck.