Get values in XML nodes using SQL - sql

I have an XML in SQL table column. I need to decode this xml and get value of particular nodes. Find my XML below
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://office/work/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I need the values of nodes: registrationId, transactionId and status and I tried this but couldn't succeed as i got empty value as result:
DECLARE #xml XML
SET #xml = 'XML here'
SELECT T.C.value('#status', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/status') T(C)
SELECT T.C.value('#trans', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/transactionId') T(C)
SELECT T.C.value('#id', 'nvarchar(100)') FROM #xml.nodes('createTransactionResponse/transactionResponse/transaction-info/registrationId') T(C)
Any help/correction would be appreciated

Your own attempt is ignoring the namespaces and does not specify the full XPath.
Try one of these approaches:
Your XML:
DECLARE #xml XML
SET #xml = '<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://office/work/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
--This is the most explicit (which is always a best way):
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT #xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/registrationId/text())[1]','nvarchar(max)') AS RegistrationId
,#xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/transactionId/text())[1]','nvarchar(max)') AS TransactionId
,#xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/status/text())[1]','nvarchar(max)') AS [Status];
--This will avoid some repeated XPath, but .nodes() produces quite some overhead:
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT ti.value('(registrationId/text())[1]','nvarchar(max)') AS RegistrationId
,ti.value('(transactionId/text())[1]','nvarchar(max)') AS TransactionId
,ti.value('(status/text())[1]','nvarchar(max)') AS [Status]
FROM #xml.nodes('/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info') A(ti);
--And this is for lazy people :-)
SELECT #xml.value('(//*:registrationId)[1]','nvarchar(max)') AS RegistrationId
,#xml.value('(//*:transactionId)[1]','nvarchar(max)') AS TransactionId
,#xml.value('(//*:status)[1]','nvarchar(max)') AS [Status];
Hint: The last one (for lazy people) uses the deep search (with //) and uses a wildcard for the namespace. This is very dangerous if the elements might occur more than once within your XML.

The XML you have is pretty complex. You have multiple namespaces, with different nodes using different ones. This means you need to use WITH XMLNAMESPACES to declare all these.
Then you need to use nodes to navigate the to needed node, prefixing them with the appropriate namespaces, till you get to transaction-info. Then you use use value to get the information.
#Status isn't what you're after here, that's for is you have something like <node status=1\>, you need to get the text() value of the node.
This results in the below:
DECLARE #X xml = '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns:createTransactionResponse
xmlns:impl="http://traffic2/traffic/services/service1"
xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
xmlns:tns="http://www.abc.ae/Activematrix/ESB/service1/1_0">
<transactionResponse>
<transaction-info>
<registrationId>R1234</registrationId>
<trialId>T12345</trialId>
<transactionId>12345</transactionId>
<transactionDate>27-02-2020:08:47</transactionDate>
<status>Confirmed</status>
</transaction-info>
</transactionResponse>
</ns:createTransactionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>';
WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
'http://traffic2/traffic/services/service1' AS impl, --YOu don't use this in the XML, but incldued anyway, for completeness
'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS ns,
'http://www.abc.ae/Activematrix/ESB/service1/1_0' AS tns) --YOu don't use this in the XML, but incldued anyway, for completeness
SELECT ti.value('(status/text())[1]','varchar(10)') AS [Status],
ti.value('(transactionId/text())[1]','int') AS Trans,
ti.value('(registrationId/text())[1]','varchar(10)') AS ID
FROM #X.nodes('SOAP-ENV:Envelope/SOAP-ENV:Body/ns:createTransactionResponse/transactionResponse/transaction-info') N(ti);

Related

T-SQL parsing XML giving blank value

I have been through the numerous questions on this issue here and none of those resolutions seem to work. I have the following XML:
DECLARE #XML xml;
SELECT #XML ='<?xml version="1.0" encoding="UTF-8"?>
<hb:MedicalAidMessage xmlns:hb="bridge.co.za/mes" Version="6.0.0">
<Claim>
<Details>
<LineItems>
<LineItem>
<HBBatchNum>2414</HBBatchNum>
</LineItem>
</LineItems>
</Details>
</Claim>
</hb:MedicalAidMessage>';
and this code to parse it:
;WITH XMLNAMESPACES ('bridge.co.za/mes' as ns)
SELECT
ISNULL(T.N.value('HBBatchNum[1]', 'INT'), 0) AS BatchNo
FROM
#XML.nodes('/Claim/Details/LineItems/LineItem[1]') AS T(N)
Which is returning a blank value instead of 2414. In the live query the XML is stored in a table column. I just cannot figure out why I am not getting the node value.
It is fairly obvious... Claim is not the root element. So use one of the following:
WITH XMLNAMESPACES ('bridge.co.za/mes' AS ns)
SELECT ISNULL(T.N.value('HBBatchNum[1]', 'int'),0) AS BatchNo
FROM #XML.nodes('/ns:MedicalAidMessage/Claim/Details/LineItems/LineItem[1]') AS T(N)
-- WITH XMLNAMESPACES ('bridge.co.za/mes' AS ns)
SELECT ISNULL(T.N.value('HBBatchNum[1]', 'int'),0) AS BatchNo
FROM #XML.nodes('/*:MedicalAidMessage/Claim/Details/LineItems/LineItem[1]') AS T(N)

SQL Query to Pars Out a Value from NTEXT (XML format) field

I have XML data in an NTEXT field (ou.ORDMODE), that I need to parse out a value (description) from. Column may contain null values. The data looks like this:
<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>
The line I have in my query is this:
CAST(REPLACE(CAST(ou.ORDMODE as NVARCHAR(MAX)),' xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF"','') as XML).value ('(/UDFValidatedValue/Description/text())[0]', 'nvarchar(100)') as Mode3,
but Mode3 column is returned as blank.
What am I doing wrong?
You could use this:
DEclare #xml xml = N'<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>';
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF')
select #xml.value ('(/UDFValidatedValue/Description)[1]', 'nvarchar(100)') as Mode3
or
select #xml.value ('declare namespace
udf="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF";
(/udf:UDFValidatedValue/udf:Description)[1]', 'nvarchar(100)') as Mode3
Demo link: Rextester
As ZLK pointed out in his comment, your own approach seems to be quite okay, just the index must be 1 instead of 0. Try it here:
ad "quite okay": Actually this is absolutely far away from "quite okay", read my hint below...
DECLARE #tbl TABLE(SomeXmlInText TEXT);
INSERT INTO #tbl VALUES
('<?xml version="1.0" encoding="utf-16"?>
<UDFValidatedValue xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF">
<Description>Export</Description>
<Value>EXP</Value>
<ValueType>String</ValueType>
</UDFValidatedValue>');
SELECT SomeXmlInText
,CAST(REPLACE(CAST(SomeXmlInText as NVARCHAR(MAX)),' xmlns="http://schemas.datacontract.org/2004/07/Accellos.Platform.UDF"','') as XML).value ('(/UDFValidatedValue/Description/text())[1]', 'nvarchar(100)') as Mode3
FROM #tbl
From your answer I take, that there is still no result...
Some possible issues:
XML is not really as you expect it
more / other namespaces
some weird no, not for you issue :-D
Hint
As you probably know TEXT, NTEXT and IMAGE are deprecated for centuries and should really not be used any more... If you have the slightest chance to change this, then you should store your XML in the appropritate XML-type...
You should not need any casts or manipulations on string level. This can be working, but is really out-dated...

How to apply XML Namespaces

I am trying to parse some xml from a SOAP API. I am quite new to XML, but i can get it to work when i only have the body of the statement. I have tried applying some simple namespaces, but I am not sure whether this is the right path to pursue.
DECLARE #XmlFile XML =
'<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Account_GetDataArrayResponse xmlns="http://e-conomic.com">
<Account_GetDataArrayResult>
<AccountData>
<Handle>
<Number>1000</Number>
</Handle>
<Number>1000</Number>
<Name>RESULTATOPGØRELSE</Name>
<Type>Heading</Type>
<DebitCredit>Debit</DebitCredit>
<IsAccessible>true</IsAccessible>
<BlockDirectEntries>false</BlockDirectEntries>
<Balance>0.00</Balance>
</AccountData>
<AccountData>
<Handle>
<Number>1001</Number>
</Handle>
<Number>1001</Number>
<Name>Omsætning</Name>
<Type>Heading</Type>
<DebitCredit>Debit</DebitCredit>
<IsAccessible>true</IsAccessible>
<BlockDirectEntries>false</BlockDirectEntries>
<Balance>0.00</Balance>
</AccountData>
</Account_GetDataArrayResult>
</Account_GetDataArrayResponse>
</soap:Body>
</soap:Envelope>'
;WITH XMLNAMESPACES( 'http://www.w3.org/2001/XMLSchema' AS e)
SELECT
AccountHandleNumber = AccountData.value('(Handle/Number)[1]', 'int')
,AccountName = AccountData.value('(Name)[1]', 'varchar(20)')
,AccountNumber = AccountData.value('(Number)[1]', 'int')
,AccountType = AccountData.value('(Type)[1]','varchar(20)')
,AccountDebitCredit = AccountData.value('(DebitCredit)[1]', 'varchar(20)')
,AccountIsAccessible = AccountData.value('(IsAccessible)[1]', 'varchar(20)')
,AccountBlockDirectEntries = AccountData.value('(BlockDirectEntries)[1]', 'varchar(20)')
,AccountBalance = AccountData.value('(Balance)[1]', 'float')
FROM #XMLFile.nodes('/Account_GetDataArrayResponse/Account_GetDataArrayResult/AccountData') AS XTbl(AccountData)
Yes, this is very much the right way!
However, you're not reading the entire XML you have - you have to really start at the root node - so try to adapt your code like this:
;WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS e,
DEFAULT 'http://e-conomic.com')
SELECT
AccountHandleNumber = AccountData.value('(Handle/Number)[1]', 'int')
.....
FROM
#XMLFile.nodes('/e:Envelope/e:Body/Account_GetDataArrayResponse/Account_GetDataArrayResult/AccountData') AS XTbl(AccountData)
First of all - you need to also take care of the <soap:Envelope> and <soap:Body> tags at the very beginning of the document - you've created a XML namespace alias of e for that - so you need to include /e:Envelope/e:Body at the beginning of your XPath expression in the .nodes() call.
Also: the <Account_GetDataArrayResponse> node declares another XML namespace which gets applied throughout the rest of the XML document - I've added that as the DEFAULT XML namespace to the query, so it will be applied to any node in the XPath expression that doesn't have an explicit XML namespace prefix associated with it

Having trouble parsing some xml

I am trying to parse some XML that has been put into a column. To simplify, I have put the XML directly into a variable and tried to parse it here.
Declare #XMLToParse XML
SELECT #XMLToParse = '<?xml version="1.0" encoding="UTF-8"?>
<atn:Transaction xmlns:atn="http://www.agcs.com/Transaction/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.agcs.com/Transaction/cosmos.xsd">
<name>AIRW0043</name>
<address>654577</address>
</atn:Transaction>'
;WITH XMLNAMESPACES(DEFAULT 'http://www.agcs.com/Transaction/cosmos.xsd',
'http://www.agcs.com/Transaction/' as atn)
Select #XMLToParse.value('data(/atn:Transaction/name)[1]','VARCHAR(100)') namecode
, #XMLToParse.value('data(/atn:Transaction/address[1]','VARCHAR(100)') addresscode
I am getting null for both values (namecode and addresscode). I should get the name value in namecode and the address value in addresscode.
The actual problem was that you're setting default namespace in WITH XMLNAMESPACES statement. That causes all elements without explicit prefix in your XPath/XQuery (f.e /name and /address) to be considered in the default namespace -while in the actual XML they are in no namespace-.
So in short, simply remove default namespace from your WITH XMLNAMESPACES statement :
Declare #XMLToParse XML
SELECT #XMLToParse = '<?xml version="1.0" encoding="UTF-8"?>
<atn:Transaction xmlns:atn="http://www.agcs.com/Transaction/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.agcs.com/Transaction/cosmos.xsd">
<name>AIRW0043</name>
<address>654577</address>
</atn:Transaction>'
;WITH XMLNAMESPACES('http://www.agcs.com/Transaction/' as atn)
Select #XMLToParse.value('data(/atn:Transaction/name)[1]','VARCHAR(100)') namecode
, #XMLToParse.value('data(/atn:Transaction/address)[1]','VARCHAR(100)') addresscode
you need to use 'text()' to get the element value
;WITH XMLNAMESPACES('http://www.agcs.com/Transaction/' as atn)
Select #XMLToParse.value('(/atn:Transaction/name/text())[1]','VARCHAR(100)') namecode
, #XMLToParse.value('(/atn:Transaction/address/text())[1]','VARCHAR(100)') addresscode

How to get specific XML namespace in XQuery in SQL Server

I have a XML that I need one specific namespace according to node like temprature with hls i need namespace of that "http://www.schema.hls.com/extension" I have tried with these
DECLARE #EventXML AS XML
SET #EventXML='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns="urn:global:test:xsd:1"
xmlns:hls="http://schema.hls.com/extension" creationDate="2007-01-25T00:00:00Z"
schemaVersion="1.0">
<TestBody>
<TestList>
<TestEvent>
<hls:temperature>20</hls:temperature>
</TestEvent>
</TestList>
</TestBody>
</ns:test>'
SELECT
OE.value('#ns','varchar(50)') + '#' + OE.value('fn:local-name(.)[1]','varchar(50)'),
OE.value('#id','varchar(50)'),
CONVERT(VARCHAR(4000),CASE WHEN OE.exist('./*') =1 THEN OE.query('./*') ELSE
OE.value('./text()[1]','varchar(100)') END)
FROM #EventXML.nodes('//TestEvent/*') TestEvent(OE)
WHERE OE.value('fn:local-name(.)[1]','varchar(50)') IN --(#tag)
(SELECT Split.a.value('.', 'VARCHAR(100)') AS extag
FROM (SELECT CONVERT(XML,'<M>' + REPLACE(ISNULL('temperature','0'), ',', '</M><M>') + '</M>') AS String
) AS A CROSS APPLY String.nodes ('/M') AS Split(a))
I am using these in SQL query window but getting only third column value 20 not get namespace by #ns
Please suggest how to get the namespace
OE.value('#ns','varchar(50)')
by these.
thanks in advanced.
Your code and XML somehow just don't quite match up - and the query is really quite confusing....
If you want to fetch the data, you must respect the XML namespaces in play. You need to declare them with a WITH XMLNAMESPACES() construct, and you need to use them in your XPath.
But also: the node you're selecting (<hls:temperature>) doesn't really have any id and ns attributes..... so of course you're not getting any values!
I tried to use a trimmed down version and I added the two attributes - just to show how to use the XML namespaces stuff in your code.
Here it comes:
DECLARE #EventXML AS XML
SET #EventXML =
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns:test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns="urn:global:test:xsd:1"
xmlns:hls="http://schema.hls.com/extension"
creationDate="2007-01-25T00:00:00Z" schemaVersion="1.0">
<TestBody>
<TestList>
<TestEvent>
<hls:temperature ns="test" id="42">20</hls:temperature>
</TestEvent>
</TestList>
</TestBody>
</ns:test>'
-- define your XML namespaces that are in play.
-- You *MUST* match the namespace definition, but the *prefixes* that you define
-- can be something else entirely than in the XML document!
-- Of course, inside your XPath, you *MUST* use the defined prefixes!
;WITH XMLNAMESPACES('urn:global:test:xsd:1' AS x1,
'http://schema.hls.com/extension' AS x2)
SELECT
OE.value('#ns', 'varchar(50)'),
OE.value('#id', 'varchar(50)')
FROM
#EventXML.nodes('/x1:test/TestBody/TestList/TestEvent/x2:*') TestEvent(OE)
This code - using the XML namespaces defined and used in your XML - produces this output:
(No column name) (No column name)
test 42
So this shows how you can access the attributes - if they are present! - on your XML nodes, even with the presence of XML namespaces.