Error "The name "soap" does not denote a namespace" if i try to parse XML in T-sql - 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)

Related

How to get value of a node with namespace prefix [duplicate]

This question already has answers here:
sql server parse xml with namespace
(2 answers)
Closed 11 months ago.
So, I have got the following query that I tried to get the value between the nodes:
<oas:Base64Assertion>235fkl53</oas:Base64Assertion>
I want to extract the value 235fkl53. I tried with the following query:
declare #x xml = '
<soapenv:Header>
<oas:Security>
<oas:Base64Assertion>
<oas:Security xmlns:oas="http://example.com">
<oas:Base64Assertion>235fkl53</oas:Base64Assertion>
</oas:Security>
</oas:Base64Assertion>
</oas:Security>
</soapenv:Header>
';
select cast(#x.value('(//oas:Base64Assertion)[1]', 'nvarchar(max)') as xml).query('//value/text()') as cdata;
But I get the following error:
Msg 2229, Level 16, State 1, Line 13 XQuery [value()]: The name "oas"
does not denote a namespace.
Any tips on how to achieve this?
You did not add the namespaces in the XML you posted so I have added them in the following example based on where I think they should be.
You need to add WITH NAMESPACE before using namespace prefixes in XQueries:
declare #x xml = '<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:oas="http://example.com">
<soapenv:Header>
<oas:Security>
<oas:Base64Assertion>
<oas:Security>
<oas:Base64Assertion>235fkl53</oas:Base64Assertion>
</oas:Security>
</oas:Base64Assertion>
</oas:Security>
</soapenv:Header>
</soapenv:Envelope>
';
WITH XMLNAMESPACES ('http://example.com' AS oas)
SELECT #x.value('(//oas:Base64Assertion)[1]', 'nvarchar(max)');
SELECT #x.value('declare namespace oas="http://example.com"; (//oas:Base64Assertion)[1]', 'nvarchar(max)');
DB<>Fiddle

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);

Convert an XML UTF-8 encoded string to XML datatype in SQL Server

Converting an XML string using CAST( AS XML) works as expected in many scenarios, but fail with an error "illegal xml character" if the string contains accented chars.
This example fails with error "XML parsing: line 2, character 8, illegal xml character":
declare #Text VARCHAR(max) =
'<?xml version="1.0" encoding="UTF-8"?>
<ROOT>níveis porém alocação</ROOT>'
select CAST(#Text AS XML)
According to XML Specification all of them are legal XML chars, but replacing accented chars with an 'X' char will result in a sucessfull CAST:
declare #MessageText VARCHAR(max) =
'<?xml version="1.0" encoding="UTF-8"?>
<ROOT>nXveis porXm alocaXXo</ROOT>'
select CAST(#MessageText AS XML)
Result: <ROOT>nXveis porXm alocaXXo</ROOT>
Moreover, the same XML but UTF-16 encoded, inexplicably works:
declare #MessageText NVARCHAR(max) =
'<?xml version="1.0" encoding="UTF-16"?>
<ROOT>níveis porém alocação</ROOT>'
select CAST(#MessageText AS XML)
Result: <ROOT>níveis porém alocação</ROOT>
Are those chars illegal in UTF-8? Or there is a better way to convert into an XML datatype?
SQL Server strips any XML Declaration prolog internally for XML data type and uses UTF-16 encoding. Here is how to handle correctly your use case.
SQL
-- Method #1
DECLARE #Text NVARCHAR(MAX) = N'<ROOT>níveis porém alocação</ROOT>';
SELECT CAST(#Text AS XML);
-- Method #2
DECLARE #MessageText NVARCHAR(MAX) =
'<?xml version="1.0" encoding="UTF-16"?>
<ROOT>níveis porém alocação</ROOT>';
SELECT CAST(#MessageText AS XML);

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)')

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