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>
Related
I found some article on internet
this url
Then I code query like that and I get same result
But when I change AS [text()] to [name]
the result contain XML tag
like this
So My question is What is [text()] in this code
Thank you.
The other current answers don't explain much about where this is coming from, or just offer links to poorly formatted sites and don't really answer the question.
In many answers around the web for grouping strings there are the copy paste answers without a lot of explanation of what's going on. I wanted to better answer this question because I was wondering the same thing, and also give insight into what is actually happening overall.
tldr;
In short, this is syntax to help transform the XML output when using FOR XML PATH which uses column names (or aliases) to structure the output. If you name your column text() the data will be represented as text within the root tag.
<row>
My record's data
<row>
In the examples you see online for how to group strings and concat with , it may not be obvious (except for the fact that your query has that little for xml part) that you are actually building an XML file with a specific structure (or rather, lack of structure) by using FOR XML PATH (''). The ('') is removing the root xml tags, and just spitting out the data.
The deal with AS [text()]
As usual, AS is acting to name or rename the column alias. In this example, you are aliasing this column as [text()]. The []s are simply SQL Server's standard column delimiters, often unneeded, except today since our column name has ()s. That leaves us with text() for our column name.
Controlling the XML Structure with Column Names
When you are using FOR XML PATH you are outputting an XML file and can control the structure with your column names. A detailed list of options can be found here: https://msdn.microsoft.com/en-us/library/ms189885.aspx
An example includes starting your column name with an # sign, such as:
SELECT color as '#color', name
FROM #favorite_colors
FOR XML PATH
This would move this column's data to an attribute of the current xml row, as opposed to an item within it. You end up with
<row color="red">
<name>tim</name>
</row>
<row color="blue">
<name>that guy</name>
</row>
So then, back to [text()]. This is actually specifying an XPath Node Test. In the context of MS Sql Server, you can learn about this designation here. Basically it helps determine the type of element we are adding this data to, such as a normal node (default), an xml comment, or in this example, some text within the tag.
An example using a few moves to structure the output
SELECT
color as [#color]
,'Some info about ' + name AS [text()]
,name + ' likes ' + color AS [comment()]
,name
,name + ' has some ' + color + ' things' AS [info/text()]
FROM #favorite_colors
FOR XML PATH
Notice we are using a few designations in our column names:
#color: a tag attribute
text(): some text for this root tag
comment(): an xml comment
info/text(): some text in a specific xml tag, <info>
The output looks like this:
<row color="red">
Some info about tim
<!--tim likes red-->
<name>tim</name>
<info>tim has some red things</info>
</row>
<row color="blue">
Some info about that guy
<!--that guy likes blue-->
<name>that guy</name>
<info>that guy has some blue things</info>
</row>
Wrapping it up, how can these tools group and concat strings?
So, with the solutions we see for grouping strings together using FOR XML PATH, there are two key components.
AS [text()]: Writes the data as text, instead of wrapping it in a tag
FOR XML PATH (''): Renames the root tag to '', or rather, removes it entirely
This gives us "XML" (air quotes) output that is essentially just a string.
SELECT name + ', ' AS [text()] -- no 'name' tags
FROM #favorite_colors
FOR XML PATH ('') -- no root tag
returns
tim, that guy,
From there, it's just a matter of joining that data back to the larger dataset from which it came.
While righting the sql query remove alias name then you got the text.
select name+',' aa from employee for xml path('')
then the result comes in xml with aa tag.
select (select name+',' from employee for xml path('')) aa
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)
Hi I have the following SQL to try and parse xml and extract the "OrderNumber". The problem i have is this xml (which i have no control over) has a wierd xml namespace. I changed it to abc.com just for this example, but its something else. Anyway, when that namepace is present, the T-SQL returns a null in the result. So it doesn't play nicely with the namespace. If I remove the namespace manually or doing a search and replace via T-SQL, it works just fine. I guess i can just do a search and replace but that solution just bothers me. Was wondering if anyone else nows a better way around this? And maybe an explanation of why it doesn't like namespaces? Would really appreciate some advice. Thanks!
Declare #Transmission xml
set #Transmission = '<Transmission>
<Requests>
<SubmitOrdersRequest>
<Orders>
<Order xmlns="http://www.abc.com">
<OrderNumber>123</OrderNumber>
</Order>
</Orders>
</SubmitOrdersRequest>
</Requests>
</Transmission>'
select #Transmission.value('(Transmission/Requests/SubmitOrdersRequest/Orders/Order/OrderNumber/text())[1]', 'varchar(100)')
Children nodes inherit the namespace of the parent, unless given a namespace themselves. you have to define namespaces using WITH XMLNAMESPACES, and properly qualify node names using them.
Declare #Transmission xml
set #Transmission = '<Transmission>
<Requests>
<SubmitOrdersRequest>
<Orders>
<Order xmlns="http://www.abc.com">
<OrderNumber>123</OrderNumber>
</Order>
</Orders>
</SubmitOrdersRequest>
</Requests>
</Transmission>';
with xmlnamespaces('http://www.abc.com' as ns1)
select #Transmission.value('(Transmission/Requests/SubmitOrdersRequest/Orders/ns1:Order/ns1:OrderNumber/text())[1]', 'varchar(100)')
Note: The reason for namespaces is that names are contextual things. Order can mean in your case a purchase but in another context it could mean display rack order. The namespace gives the name more uniqueness.
I have an XML variable with only one element in it. I need to check if this element has a particular attribute, and if it does, i need to check if that attribute has a specific value, and if it does, i need to remove that attribute from the XML element.
So lets say I have
DECLARE #Xml XML
SET #XML =
'<person
FirstName="Harvey"
LastName="Saayman"
MobileNumber="Empty"
/>'
The MobileNumber attribute may or may not be there, if it is, and the value is "Empty", i need to change my XML variable to this:
'<person
FirstName="Harvey"
LastName="Saayman"
/>'
I'm a complete SQL XML noob and have no idea how to go about this, any ideas?
Use the modify() DML clause to modify the XML nodes. On this case something like:
SET #XML.modify('delete (/person/#MobileNumber)[1]')
This XML workshop can be helpfull to have a deeper understanding of the DML clauses delete, insert, replace, etc.
SET #XML.modify('delete /person/#MobileNumber[. = "Empty"]')
I have a table with a mix of escaped and non-escaped XML. Of course, the data I need is escaped. For example, I have:
<Root>
<InternalData>
<Node>
<ArrayOfComment>
<Comment>
<SequenceNo>1</SequenceNo>
<IsDeleted>false</IsDeleted>
<TakenByCode>397</TakenByCode>
</Comment>
</ArrayOfComment>
</Node>
</InternalData>
</Root>
As you can see, the data in the Node tag is all escaped. I can use a query to obtain the Node data, but how can I convert it to XML in SQL so that it can be parsed and broken up? I'm pretty new to using XML in SQL, and I can't seem to find any examples of this.
Thanks
You have not given enough information about your end goal, but this will get you very close. FYI - You had two missing ; both after comment>
declare #xml xml
set #xml = '
<Root>
<InternalData>
<Node>
<ArrayOfComment>
<Comment>
<SequenceNo>1</SequenceNo>
<IsDeleted>false</IsDeleted>
<TakenByCode>397</TakenByCode>
</Comment>
</ArrayOfComment>
</Node>
</InternalData>
</Root>
'
select convert(xml, n.c.value('.', 'varchar(max)'))
from #xml.nodes('Root/InternalData/Node/text()') n(c)
Output
<ArrayOfComment>
<Comment>
<SequenceNo>1</SequenceNo>
<IsDeleted>false</IsDeleted>
<TakenByCode>397</TakenByCode>
</Comment>
</ArrayOfComment>
The result is an XML column that you can put into a variable or cross-apply into directly to get data from the XML fragment.
Your best bet might be to look into a HTML Decoding UDF. I did a quick search and found this one:
http://www.andreabertolotto.net/Articles/HTMLDecodeUDF.aspx
You may want to modify it so it only decodes > and <. The one above seems to go above and beyond your needs.
UPDATE
#Cyberkiwi's solution seems to be a bit cleaner. I will leave this up in case the version of SQL Server you are running doesn't support his solution.