Parse xml in SQL server using Openxml - sql-server

Please let me know why my query doesn't work.
Following is my XML.
Trying to parse it in SQL Server.
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<PostEDIDataToEIP xmlns="http://tempuri.org/">
<xmlRequestDetails>
<Applicant>
<ApplyData>
<RefNo>86</RefNo>
<ProfileSnapshot>
<Name>King</Name>
<ResumeHeadline>RESUME HEADLINE.......................</ResumeHeadline>
</ProfileSnapshot>
</ApplyData>
</Applicant>
</xmlRequestDetails>
</PostEDIDataToEIP>
</soap:Body>
</soap:Envelope>
set #xmlRequestDetails='Above xml'
set #xPath='/soap:Envelope/soap:Body/PostEDIDataToEIP/xmlRequestDetails/Applicant/ApplyData'
Exec sp_xml_preparedocument #XML_Hndl OUTPUT, #xmlRequestDetails
select * into #jobdetails FROM OPENXML(#XML_Hndl,#xPath,2) with (RefNo smallint)
select * from #jobdetails
set #xPath='/soap:Envelope/soap:Body/PostEDIDataToEIP/xmlRequestDetails/Applicant/ApplyData/ProfileSnapshot'
select * into #personaldetails FROM OPENXML(#XML_Hndl,#xPath,2) with (Name varchar(100), ResumeHeadline varchar(100))
select * from #personaldetails
Receiving the following error.
Msg 6603, Level 16, State 2, Procedure sp_forintegration, Line 31
XML parsing error: Reference to undeclared namespace prefix: 'soap'.
The statement has been terminated.
Msg 208, Level 16, State 0, Procedure sp_forintegration, Line 33
Invalid object name '#jobdetails'.
This xml is getting inserted into Sql server table XML column.
I feel i am doing some mistake while parsing, please let me know what can be done, or how to parse.
Thanks/Regards,
Prasanth Kumar

You need to declare the namespace prefixes when you prepare the XML document, something along the following:
SET #rootxmlns = '<root xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"/>'
Exec sp_xml_preparedocument #XML_Hndl OUTPUT, #xmlRequestDetails, #rootxmlns
See here for a more detailed example.

Related

Can't read a property value from XML

I'm trying to get the value from the property Success but I can't, I don't know where I'm wrong.
This is my code
DECLARE #Response VARCHAR(8000) = '<?xml version="1.0" encoding="utf-8"?>
<Result xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
<Success>true</Success>
</Result>'
DECLARE #xml TABLE (
Content XML
)
INSERT INTO #xml
SELECT CAST(#Response AS XML)
SELECT
Content.value('(/Result/Success)[1]', 'BIT')
FROM #xml
The property Success is bool type
I'm trying with different scope types (nvarchar, varchar, bit, etc..)
This is what I expecting
or
Please try the following solution.
Notable points:
It is always better to use XML data type instead of the VARCHAR(..) for XML
data.
All XML elements are bound to the default namespace even if we don't
see it explicitly. That's why we need to specify it via
XMLNAMESPACES clause.
It is always better to use text() in the XPath expressions for XML
elements for performance reasons. Peculiarity of the MS SQL Server.
It is possible to omit XML prolog declaration completely. SQL Server
doesn't store it.
SQL
DECLARE #Response XML = '<?xml version="1.0" encoding="utf-8"?>
<Result xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/">
<Success>true</Success>
</Result>';
DECLARE #xml TABLE (Content XML);
INSERT INTO #xml
SELECT #Response;
;WITH XMLNAMESPACES (DEFAULT 'http://tempuri.org/')
SELECT result = Content.value('(/Result/Success/text())[1]', 'BIT')
FROM #xml;
Output
result
1

sql server parse xml with namespace

I'm trying to parse the following xml to get the first tag following the s:Body tag (in this case I'm looking for the string queryEE, in other messsages with the same Envelope/Body structure it will be different)
I began playing with it with something like this:
declare #text varchar(max)
set #text = N'
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<queryEE xmlns="http://xx.gob.gcaba.xx/">
<codeEE xmlns="">xxxx</codeEE>
</queryEE>
</s:Body>
</s:Envelope>'
declare #x xml
set #x = cast(#text as xml)
select #x.query('/s:Envelope')
But I get the error:
Msg 2229, Level 16, State 1, Line 16
XQuery [query()]: The name "s" does not denote a namespace.
Seems like I'm having troubles with the namespace stuff
When I try with select #x.query('/Envelope') I don't get any results at all
Thanks to the answers I got from #shnugo I could finally solve it with:
select #x.value('local-name((/*:Envelope/*:Body/*)[1])','nvarchar(100)')
Try it like this:
--your declaration
declare #text varchar(max)
set #text = N'
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<queryEE xmlns="http://xx.gob.gcaba.xx/">
<codeEE xmlns="">xxxx</codeEE>
</queryEE>
</s:Body>
</s:Envelope>'
declare #x xml
set #x = cast(#text as xml);
--The query
WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS s
,'http://xx.gob.gcaba.xx/' AS innerDflt)
select #x.value('(/s:Envelope/s:Body/innerDflt:queryEE/codeEE/text())[1]','nvarchar(100)');
Some background:
Your XML is a bit weird looking at the namespaces... if the construction is under your control, it would be worth to start here.
There is a namespace s: to define <Envelope> and <Body>. That is fine so far., But then the element <queryEE> defines a default namespace (no prefix!) and the embedded <codeEE> defines another (but empty!) default namespace. I'm pretty sure, that this empty namespaces is created within a query by combining XMLs together...
The default namespace tells the engine, that all nodes without a specific prefix are living within this namespace. So we have to address that.
My code is using WITH XMLNAMESPACES to declare all namespaces occuring in the XML. Different to the original XML I define a prefix (innerDflt) for the first defualt namespace. That means, we can address <innerDflt:queryEE>. The embedded element does not need a namespace. It is living within an empty default (=> no) namespace.
All this said, I just want to point out, that you can use a wildcard too:
select #x.value('(/*:Envelope/*:Body/*:queryEE/*:codeEE/text())[1]','nvarchar(100)')
And you might even use a deep search
select #x.value('(//*:codeEE/text())[1]','nvarchar(100)')
But the general advise is: Be as specific as possible.
Declare your namespace again when using xquery for xml with namespaces.
declare #text varchar(max)
set #text = N'
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<queryEE xmlns="http://xx.gob.gcaba.xx/">
<codeEE xmlns="">xxxx</codeEE>
</queryEE>
</s:Body>
</s:Envelope>'
declare #x xml
set #x = cast(#text as xml)
select #x.query('declare default element namespace "http://schemas.xmlsoap.org/soap/envelope/";
/Envelope')

extract xml element in sql query

I have the following xml stored in my database table.
I am trying to extract the Productid from the xml , I have been unsuccessful.
Could you tell me what changes I need to make to make the query work ?
XML :
DECLARE #Response VARCHAR(MAX) = '<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ProductId xsi:type="xsd:long" xmlns="http://nn.service.eservice_v1">30061</ProductId>
</Response>'
Sql Query :
select
CONVERT(XML,CONVERT(NVARCHAR(max),#Response)).value('(/Response/ProductId)[1]','nvarchar(500)') as ProviderId
You want to store the XML in an XML type instead of converting from varchar. If you are able to strip out all the namespace stuff, the following will work.
DECLARE #Response XML = '<Response><ProductId>30061</ProductId></Response>'
SELECT #Response.value('(//ProductId)[1]','nvarchar(500)') as ProviderId
If you are not able to strip out all the namespace stuff, then you will need to include the WITH XMLNAMESPACES clause
DECLARE #Response XML = '<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ProductId xsi:type="xsd:long" xmlns="http://nn.service.eservice_v1">30061</ProductId>
</Response>'
;WITH XMLNAMESPACES ('http://nn.service.eservice_v1' as e,
'http://www.w3.org/2001/XMLSchema-instance' as xsi)
SELECT #Response.value('(/Response/e:ProductId)[1]','nvarchar(500)') as ProviderId

Query an xml (stored in varchar) in sql server

I have a database table which is structured as shown below :
TransactionId Product ErrorXML(stored as varchar)
The ErrorXML has the structure below :
<?xml version="1.0" encoding="utf-16"?>
<GetResponse
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Error>
<ErrorCode>1809</ErrorCode>
<Description>Could not generate transaction.</Description>
</Error>
<Success>false</Success>
<ContractNumber />
</GetResponse>
I would like to write a query to extract the ErrorCode element and ErrorDescription element for each of the Errorxml row .
Is there a way I can do it ?
I did some research and tried writing a query something like this and it wouldnt work , gives out a null:
select CONVERT(XML,CONVERT(NVARCHAR(max),ErrorXML)).value('(/GetResponse/Error/Description)[1]','nvarchar(100)') as ErrorDescription
from ErrorLog E
Here's a working SQL Fiddle
The Output
ErrorCode ErrorDescription
1809 Could not generate transaction.
2119 Credit Card Overlimit.Payment Failed.
The SQL
See the SQL fiddle for why we have to use a TABLE variable due to your data being in VARCHAR and not XML datatype.
--SELECT * FROM Transactions
DECLARE #ErrorDataTable TABLE
(
ErrorXml XML NOT NULL,
ErrorCode NVARCHAR(50) NULL,
ErrorDescription NVARCHAR(200) NULL
)
INSERT
INTO #ErrorDataTable(ErrorXml)
SELECT
CONVERT(XML,CONVERT(NVARCHAR(max),T.ErrorXML))
FROM Transactions T
-- SELECT * FROM #ErrorDataTable
SELECT
T.c.value('ErrorCode[1]', 'nvarchar(50)') as ErrorCode
, T.c.value('Description[1]', 'nvarchar(200)') as ErrorDescription
FROM #ErrorDataTable e
OUTER APPLY e.ErrorXml.nodes('/GetResponse/Error') T(c);
Previous Answer (deprecated)
Following Works. I'll try to put a SQLFiddle together.
DECLARE #ErrorXML as VARCHAR(MAX);
DECLARE #x as XML;
SET #ErrorXML ='<?xml version="1.0" encoding="utf-16"?>
<GetResponse
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Error>
<ErrorCode>1809</ErrorCode>
<Description>Could not generate transaction.</Description>
</Error>
<Success>false</Success>
<ContractNumber />
</GetResponse>';
SET #x = CONVERT(XML,CONVERT(NVARCHAR(max),#ErrorXML))
SELECT
T.c.value('ErrorCode[1]', 'nvarchar(50)') as ErrorCode
, T.c.value('Description[1]', 'nvarchar(200)') as ErrorDescription
FROM #x.nodes('/GetResponse/Error') T(c);
RESULTS:
ErrorCode ErrorDescription
1809 Could not generate transaction.

Query XML parameter in SQL

This seemed like a great idea on Friday afternoon but I'm having a bit of trouble. I've not used SQL XML querying before so I may of just done something incredibly stupid. Basically I want to pass a series of strings to query a table into a Stored Procedure.
I thought about this for a bit, considered using a CSV and then decided to attempt to do this using XML. So My XML looks like:
<Root>
<string>value</string>
<string>value</string>
<string>value</string>
<string>value</string>
</Root>
I'm passing this into a stored proc as an XML value type:
CREATE PROCEDURE usp_UpdateHotelImages
-- Add the parameters for the stored procedure here
#hotelID int,
#imageIDs xml
AS
BEGIN
so I want to shred the XML into a table of strings.
My SQL looks like this:
SELECT Child.value('(string)[1]', 'varchar(200)')
FROM #imageIDs.nodes('/Root/') AS N(Child))
But I keep getting the error message XQuery [nodes()]: Syntax error near '<eof>', expected a "node test".
I may well be doing something incredibly stupid here so any help will be gratefully received.
Update
I've broken it down into a single query to help:
DECLARE #imageIDs xml
SET #imageIDs = '<Root>
<string>value</string>
<string>value</string>
<string>value</string>
<string>value</string>
</Root>'
SELECT Child.value('(string)[1]', 'varchar(200)')
FROM #imageIDs.nodes('/Root/') AS N(Child)
The problem is the last / in the nodes function.
SELECT Child.value('(string)[1]', 'varchar(200)')
FROM #imageIDs.nodes('/Root') AS N(Child)
or alternatively
SELECT Child.value('(.)[1]', 'varchar(200)')
FROM #imageIDs.nodes('/Root/*') AS N(Child)
Depending on what you're trying to achieve.
The error is because of trailing / in your nodes expressions. It should just be /Root.
BTW, I think you are looking for a SELECT to return the values as a table which is achieved by the following:
DECLARE #imageIDs XML
SELECT #imageIDs = '
<Root>
<string>value</string>
<string>value2</string>
<string>value3</string>
<string>value4</string>
</Root>'
SELECT
Child.value('(.)[1]', 'varchar(200)')
FROM #imageIDs.nodes('/Root/string') AS N(Child)
Results:
value
value2
value3
value4
(4 row(s) affected)

Resources