Validating empty xml by xsd in SQL Server - sql

I have following simple xsd schema:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="MyUrl" type="xsd:anyURI"/>
</xsd:schema>'
I have SQL Server 2008 R2 and I want to validate my variable against this schema. It works, but variable x got validated even if it's empty or whitespace, however empty xml isn't valid against this schema.
Why I got these results?
TSQL code:
CREATE XML SCHEMA COLLECTION dbo.xsdTest AS
N'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="MyUrl" type="xsd:anyURI"/>
</xsd:schema>'
GO
DECLARE #x XML(dbo.xsdTest)
SET #x = ' ' --no error

By default the XML data type accepts XML fragments as valid XML.
The XML data can contain multiple zero or more elements at the top
level.
You can specify that the XML must be a valid XML document like this.
declare #x xml(document dbo.xsdTest)
set #x = '' -- error here

Related

Error: XML declaration must be the first node in the document, and no white space characters are allowed to appear before it

The functionality is like, we could write the plane SQL queries in XML and then we could import that xml in Product to see the changes in database.
In Update statement, I need to use the below update query. But getting 'XML declaration must be the first node in the document' error when trying to set column value.
<?xml version="1.0" encoding="utf-8"?>
<Metadata ClientSchemaVersion="1.1" Name="DummyName">
<Differences>
<Updates>
---
---
Begin
Update TABLE_NAME
Set //In next line, error is comming
ColumnName='<?xml version=''1.0'' encoding=''utf-16le'' ?><scenario xmlns='Text'><id>12345</id><title>
--
--
--
WHERE Condition
END
</Differences>
</Metadata>
How I could achieve that use case. Could someone help me out here.
Note- The same Update Query is working fine if ran in SQL Server directly
Two XML declarations (<?xml ... ?>) are not allowed in an XML document. Only one is permitted, and it must be at the very top of the document.
Here are two ways you can repair your XML:
Remove the second XML declaration. The default for that SQL-embedded XML will then be XML version 1.0 with UTF-8 encoding.
If the processing application supports it, wrap the SQL in a CDATA section such that the SQL-embedded XML document is no longer parsed as XML.
<![CDATA[
... SQL with embedded XML that can contain its own XML declaration ...
]]>
See also
Error: The processing instruction target matching "[xX][mM][lL]" is not allowed

SQL SERVER xml with CDATA

I have a table in my database with a column containing xml. The column type is nvarchar(max). The xml is formed in this way
<root>
<child>....</child>
.
.
<special>
<event><![CDATA[text->text]]></event>
<event><![CDATA[text->text]]></event>
...
</special>
</root>
I have not created the db, I cannot change the way information is stored in it but I can retrieve it with a select. For the extraction I use
select cast(replace(xml,'utf-8','utf-16')as xml)
from table
It works well except for cdata, whose content in the query output is: text -> text
Is there a way to retrieve also the CDATA tags?
Well, this is - as far as I know - not possible on normal ways...
The CDATA section has one sole reason: include invalid characters within XML for lazy people...
CDATA is not seen as needed at all and therefore is not really supported by normal XML methods. Or in other words: It is supported in the way, that the content is properly escaped. There is no difference between correctly escaped content and not-escaped content within CDATA actually! (Okay, there are some minor differences like including ]]> within a CDATA-section and some more tiny specialties...)
The big question is: Why?
What are you trying to do with this afterwards?
Try this. the included text is given as is:
DECLARE #xml XML =
'<root>
<special>
<event><![CDATA[text->text]]></event>
<event><![CDATA[text->text]]></event>
</special>
</root>'
SELECT t.c.query('text()')
FROM #xml.nodes('/root/special/event') t(c);
So: Please explain some more details: What do you really want?
If your really need nothing more than the wrapping CDATA you might use this:
SELECT '<![CDATA[' + t.c.value('.','varchar(max)') + ']]>'
FROM #xml.nodes('/root/special/event') t(c);
Update: Same with outdated FROM OPENXML
I just tried how the outdated approach with FROM OPENXML handles this and found, that there is absolutely no indication in the resultset, that the given text was within a CDATA section originally. The "Some value here" is exactly returned in the same way as the text within CDATA:
DECLARE #doc XML =
'<root>
<child>Some value here </child>
<special>
<event><![CDATA[text->text]]></event>
<event><![CDATA[text->text]]></event>
</special>
</root>';
DECLARE #hnd INT;
EXEC sp_xml_preparedocument #hnd OUTPUT, #doc;
SELECT * FROM OPENXML (#hnd, '/root',0);
EXEC sp_xml_removedocument #hnd;
This is how to include cdata on child nodes in XML, using pure SQL. But; it's not ideal.
SELECT 1 AS tag,
null AS parent,
'10001' AS 'Customer!1!Customer_ID!Element',
'AirBallon Captain' AS 'Customer!1!Title!cdata',
'Customer!1' = (
SELECT
2 AS tag,
NULL AS parent,
'Wrapped in cdata, using explicit' AS 'Location!2!Title!cdata'
FOR XML EXPLICIT)
FOR XML EXPLICIT, ROOT('Customers')
CDATA is included, but Child element is encoded using
>
instead of >
Which is so weird from a sensable point of view. I'm sure there are technical explanations, but they are stupid, because there is no difference in the FOR XML specification.
You could include the option type on the inner child node and then loose cdata too..
BUT WHY OH WHY?!?!?!?! would you (Microsoft) remove cdata, when I just added it?
<Customers>
<Customer>
<Customer_ID>10001</Customer_ID>
<Title><![CDATA[AirBallon Captain]]></Title>
<Location>
<Title><![CDATA[wrapped in cdata, using explicit]]></Title>
</Location>
</Customer>
</Customers>

SQL Server Grabbing Value from XML parameter to use in later query

I am really new to SQL Server and stored procedures to begin with. I need to be able to parse an incoming XML file for a specific element's value and compare/save it later in the procedure.
I have a few things stacked against me. One the Element I need is buried deeply inside the document. I have had no luck in searching for it by name using methods similar to this:
select CurrentBOD = c.value('(local-name(.))[1]', 'VARCHAR(MAX)'),
c.value('(.)[1]', 'VARCHAR(MAX)') from #xml.nodes('PutMessage/payload/content/AcknowledgePartsOrder/ApplicationArea/BODId') as BODtable(c)
It always returns null.
So, I am trying something similar to this:
declare #BODtable TABLE(FieldName VARCHAR(MAX),
FieldValue VARCHAR(MAX))
SELECT
FieldName = nodes.value('local-name(.)', 'varchar(50)'),
FieldValue = nodes.value('(.)[1]', 'varchar(50)')
FROM
#xml.nodes('//*') AS BODtable(nodes)
declare #CurrentBOD VARCHAR(36)
set #CurrentBOD = ''
SET #CurrentBOD = (SELECT FieldValue from #BODtable WHERE FieldName = 'BODId')
This provides me the list of node names and values correctly (I test this in a query and BODtable has all elements listed with the correct values), but when I set #CurrentBOD it comes up null.
Am I missing an easier way to do this? Am I messing these two approaches up somehow?
Here is a part of the xml I am parsing for reference:
<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" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity- secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401- wss-wssecurity-utility-1.0.xsd">
<soap:Header>
<payloadManifest xmlns="???">
<c contentID="Content0" namespaceURI="???" element="AcknowledgePartsOrder" version="4.0" />
</payloadManifest>
<wsa:Action>http://www.starstandards.org/webservices/2005/10/transport/operations/PutMessage</wsa:Action>
<wsa:MessageID>uuid:df8c66af-f364-4b8f-81d8-06150da14428</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>???</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp wsu:Id="Timestamp-bd91e76f-c212-4555-9b23-f66f839672bd">
<wsu:Created>2013-01-03T21:52:48Z</wsu:Created>
<wsu:Expires>2013-01-03T21:53:48Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu="???" wsu:Id="???">
<wsse:Username>???</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">???</wsse:Password>
<wsse:Nonce>???</wsse:Nonce>
<wsu:Created>2013-01-03T21:52:48Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<PutMessage xmlns="??????">
<payload>
<content id="???">
<AcknowledgePartsOrder xmlns="???" xmlns:xsi="???" xsi:schemaLocation="??? ???" revision="???" release="???" environment="???n" lang="en-US" bodVersion="???">
<ApplicationArea>
<Sender>
<Component>???</Component>
<Task>???</Task>
<ReferenceId>???</ReferenceId>
<CreatorNameCode>???</CreatorNameCode>
<SenderNameCode>???</SenderNameCode>
<DealerNumber>???</DealerNumber>
<PartyId>???</PartyId>
<LocationId />
<ServiceId />
</Sender>
<CreationDateTime>2013-01-03T21:52:47</CreationDateTime>
<BODId>71498800-c098-4885-9ddc-f58aae0e5e1a</BODId>
<Destination>
<DestinationNameCode>???</DestinationNameCode>
You need to respect the XML namespaces!
First of all, your target XML node <BODId> is inside the <soap:Envelope> and <soap:Body> tags - both need to be included in your selection.
Secondly, both the <PutMessage> as well as the <AcknowledgePartsOrder> nodes appear to have default XML namespaces (those xmlns=.... without a prefix) - and those must be respected when you select your data using XPath.
So assuming that <PutMessage xmlns="urn:pm"> and <AcknowledgePartsOrder xmlns="urn:apo"> (those are just guesses on my part - replace with the actual XML namespaces that you haven't shown use here), you should be able to use this XPath to get what you're looking for:
;WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS soap,
'urn:pm' AS ns, 'urn:apo' AS apo)
SELECT
XC.value('(apo:BODId)[1]', 'varchar(100)')
FROM
#YourXmlVariable.nodes('/soap:Envelope/soap:Body/ns:PutMessage/ns:payload/ns:content/apo:AcknowledgePartsOrder/apo:ApplicationArea') AS XT(XC)
This does return the expected value (71498800-c098-4885-9ddc-f58aae0e5e1a) in my case.

SQL Server Script - select xml child nodes of root as (n)varchar

I need to create a SQL Server script and a part of the script is selecting the names of the immediate child nodes of the root node and convert it to a (n)varchar. I don't need the attributes or content of the node.
This is an example of the xml:
declare #XML xml
set #XML =
'
<config>
<module1 />
<module2 />
</config>
'
I want the result like this:
module1
module2
Note that the xml is not hardcoded and can have many different child nodes.
I've already taken a look at this (msdn)link but at first sight it doesn't seem possible with those XML methods.
Many thanks,
Kjell
If you want the XML of the child nodes you mentioned you can use the Query method, for example;
select
cast(#XML.query('//GuiConfiguration/Activities') as nvarchar(max)),
cast(#XML.query('//GuiConfiguration/Reservations') as nvarchar(max))
EDIT: Answer to refined question
To get the names of the immediate child nodes of the root you can use this;
select
cast(t.c.query('local-name(.)') as nvarchar(max))
from
#xml.nodes('//*[1]/child::node()') as t(c)

XML parsing error in SQL Server stored procedure

I want to parse the below XML in a SQL Server stored procedure and update some tables based on this XML. I have implemented the same using OPENXML but now there is one more line added to the beginning of the XML, because of which am getting unexpected errors. Is it possible to somehow skip the first tag alone while parsing
Parsing code :
set #Lead= (select lead
from openxml(#DOCHANDLE,'/DBO.TBLLEADS',2) with (lead INT 'LEAD'))`
XML here:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LEADS>
<LEAD>6680299</LEAD>
<JOBNO>50919</JOBNO>
<BEGINDATE>4-04-2013</BEGINDATE>
<ENDDATE>04/14/2013</ENDDATE>
</LEADS>
Well, not the most elegant solution, but will get it back to working:
Before you prepare your XML document, run this statement on the variable containing the XML:
SET #XMLVariable = REPLACE(#XMLVariable, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', '')
Basically you're leveraging the REPLACE function to replace the unnecessary header string with nothing.