How to apply XML Namespaces - sql

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

Related

Error "The name "soap" does not denote a namespace" if i try to parse XML in T-sql

When i try to parse XML in T-sql I get error "The name "soap" does not denote a namespace"
This is my XML and query. Answer, please, how can I parse such XML. Thanks.
DECLARE #xml xml
SET #xml =
'<soap:Envelope xmlns:soap=http://schemas.xmlsoap.org/soap/envelope/>
<soap:Body>
<GetWebLinkResponse xmlns=http://test.org/ xmlns:ns2=http://schemas.datacontract.org/2004/07/.Configuration xmlns:ns3=http://schemas.datacontract.org/2004/07/.Configuration.Results xmlns:ns4=http://schemas.microsoft.com/2003/10/Serialization/>
<GetWebLinkResult>
<ns3:Error>
<ns2:Id>0</ns2:Id>
<ns2:Text>Success.</ns2:Text>
</ns3:Error>
<ns3:Url>
https://example.com&content=true
</ns3:Url>
</GetWebLinkResult>
</GetWebLinkResponse>
</soap:Body>
</soap:Envelope>'
SELECT
-- n.value('(./Code/text())[1]','int') as CODE
--,
n.value('ns3:Url','varchar(1000)')as NAME
FROM #xml.nodes('/soap:Envelope/soap:Body/GetWebLinkResponse/GetWebLinkResult') as a(n)
First of all - your XML is invalid - your namespace definitions must be in double quotes:
DECLARE #xml xml
SET #xml =
'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetWebLinkResponse xmlns="http://test.org/" xmlns:ns2="http://schemas.datacontract.org/2004/07/.Configuration" xmlns:ns3="http://schemas.datacontract.org/2004/07/.Configuration.Results" xmlns:ns4="http://schemas.microsoft.com/2003/10/Serialization/">
<GetWebLinkResult>
<ns3:Error>
<ns2:Id>0</ns2:Id>
<ns2:Text>Success.</ns2:Text>
</ns3:Error>
<ns3:Url>
https://example.com&content=true
</ns3:Url>
</GetWebLinkResult>
</GetWebLinkResponse>
</soap:Body>
</soap:Envelope>';
Once you've corrected that, you'll need to define all relevant XML namespaces for your T-SQL query. Relevant XML namespaces are all explicitly used namespaces (like soap: and ns3:), as well as default namespaces like the xmlns="http://test.org/" on your <GetWebLinkResponse> node.
When you declare them all - the T-SQL query should work:
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS soap,
'http://schemas.datacontract.org/2004/07/.Configuration.Results' as ns3,
DEFAULT 'http://test.org/')
SELECT
n.value('(ns3:Url)[1]', 'varchar(1000)') AS NAME
FROM
#xml.nodes('/soap:Envelope/soap:Body/GetWebLinkResponse/GetWebLinkResult') as a(n)

Get values in XML nodes using 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);

Getting node value in xml column

Please let me know why the following XML query is not fetching any result.
I am trying to get value EffectiveUserName tag.
DECLARE #MyXML XML
SET #MyXML = '<PropertyList xmlns="urn:schemas-microsoft-com:xml-analysis" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<Catalog>name</Catalog>
<Timeout>600</Timeout>
<Format>Native</Format>
<DbpropMsmdFlattened2>false</DbpropMsmdFlattened2>
<Cube>Model</Cube>
<DbpropMsmdOptimizeResponse>1</DbpropMsmdOptimizeResponse>
<DbpropMsmdActivityID>68A6900B-20F8-4A02-AEC3-7C56B2D3C5D5</DbpropMsmdActivityID>
<DbpropMsmdRequestID>A0D1E07F-AE29-4CCA-AEE4-3B79D97CA426</DbpropMsmdRequestID>
<DbpropMsmdCurrentActivityID>68A6900B-20F8-4A02-AEC3-7C56B2D3C5D5</DbpropMsmdCurrentActivityID>
<LocaleIdentifier>1033</LocaleIdentifier>
<EffectiveUserName>userid#domainname.com</EffectiveUserName>
<sspropinitappname>PowerBI</sspropinitappname>
</PropertyList>'
select #MyXML.value('(/PropertyList/EffectiveUserName)[1]','varchar(max)')
Your XML has a default namespace that you must respect and include in your query!
<PropertyList xmlns="urn:schemas-microsoft-com:xml-analysis"
***********************************************
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
Use this code to grab the value you're looking for by defining the default XML namespace for your query:
;WITH XMLNAMESPACES(DEFAULT 'urn:schemas-microsoft-com:xml-analysis')
SELECT
#MyXML.value('(/PropertyList/EffectiveUserName)[1]', 'varchar(50)')
You can ignore the namespace by using *: before the tag names:
select #MyXML.value('(/*:PropertyList/*:EffectiveUserName)[1]','varchar(max)')

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.