Having trouble parsing some xml - sql

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

Related

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

How to get value from ntext (in xml format) column in sql

I have a column in my SQL database that is called Triggers_xml_data and its type is ntext. The column is in a xml format and I am trying to get a value from a certain part of the xml. I seen an example of this being done without a column like this:
declare #fileContent xml
set #fileContent ='<my:Header>
<my:Requestor>Mehrlein, Roswitha</my:Requestor>
<my:RequestorUserName>SJM\MehrlR01</my:RequestorUserName>
<my:RequestorEmail>RMehrlein#SJM.com</my:RequestorEmail>
<my:HRContact>Roswita Mehrlein, Beatrice Porta</my:HRContact>
<my:Entity>SJM Germany</my:Entity>
<my:Department>HR/Administration</my:Department>
<my:PositionTitle>Sales Representative</my:PositionTitle>
<my:JobDescription>x0lGQRQAAAABAAAAAAAAAAAeAQAyAAAAVgBAAAAA=</my:JobDescription>
<my:PositionDepartment>Sales</my:PositionDepartment>'
 
;WITH XMLNAMESPACES ('http://schemas.microsoft.com/office/infopath/2003/myXSD/2005-08-29T12-58-51' as my)
select #fileContent.value('(//my:PositionDepartment)[1]', 'varchar(255)')
But I want to select my column like this:
Declare #filevalue xml
select de.triggers_xml_data
from dbo.DEPLOYMENT_ENVIRONMENT as de
But this is not working and I tried to use this #filecontent.value('(//value)[1]','varchar(255)') and making it equal the column value, I have tried casting it but I can't find a way to do this. Is this possible?
When I do this:
SELECT
CAST(
REPLACE(CAST(de.TRIGGERS_XML_DATA AS VARCHAR(MAX)), 'encoding="utf-16"', '')
AS XML).value('(triggers/triggerDefinition/config/item/value)[1]', 'NVARCHAR(max)') as Item, de.ENVIRONMENT_ID
from dbo.DEPLOYMENT_ENVIRONMENT as de
where de.ENVIRONMENT_ID = 19234819
I am getting a null value returned.
Here is an example of what my xml could look like:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration xml:space="preserve">
<triggers>
<defined>true</defined>
<triggerDefinition>
<id>1</id>
<name>After successful deployment</name>
<userDescription/>
<isEnabled>true</isEnabled>
<pluginKey>com.atlassian.bamboo.triggers.atlassian-bamboo-triggers:afterSuccessfulDeployment</pluginKey>
<triggeringRepositories/>
<config>
<item>
<key>deployment.trigger.afterSuccessfulDeployment.triggeringEnvironmentId</key>
<value>19234819</value>
</item>
</config>
</triggerDefinition>
</triggers>
<bambooDelimiterParsingDisabled>true</bambooDelimiterParsingDisabled>
</configuration>
The XML, as you posted it, is not valid. Your code example does not work... It is not allowed to use a namespace prefix without a namespace declaration. Furthermore your example misses the closing Header-tag...
I corrected this...
DECLARE #yourTbl TABLE(ID INT, YourXML NTEXT);
INSERT INTO #yourTbl VALUES
(1,N'<my:Header xmlns:my="DummyUrl">
<my:Requestor>Mehrlein, Roswitha</my:Requestor>
<my:RequestorUserName>SJM\MehrlR01</my:RequestorUserName>
<my:RequestorEmail>RMehrlein#SJM.com</my:RequestorEmail>
<my:HRContact>Roswita Mehrlein, Beatrice Porta</my:HRContact>
<my:Entity>SJM Germany</my:Entity>
<my:Department>HR/Administration</my:Department>
<my:PositionTitle>Sales Representative</my:PositionTitle>
<my:JobDescription>x0lGQRQAAAABAAAAAAAAAAAeAQAyAAAAVgBAAAAA=</my:JobDescription>
<my:PositionDepartment>Sales</my:PositionDepartment>
</my:Header>');
--Lazy approach
SELECT ID
,CAST(CAST(YourXml AS NVARCHAR(MAX)) AS XML).value(N'(//*:PositionDepartment)[1]','nvarchar(max)')
FROM #yourTbl;
--explicit approach
WITH XMLNAMESPACES('DummyUrl' AS my)
SELECT ID
,CAST(CAST(YourXml AS NVARCHAR(MAX)) AS XML).value(N'(/my:Header/my:PositionDepartment)[1]','nvarchar(max)')
FROM #yourTbl
Some Background
If possible you should not store XML in other format than XML and further more one should avoid NTEXT, as it is depricated since SS2005!.
You have to cast NTEXT to NVARCHAR(MAX) first, than cast this to XML. The second will break, if the XML is not valid. That means: If the XML is really the way you posted it, this cannot work!
UPDATE: String-based approach, if XML does not work
If you cannot cast this to XML you might try this
--String based
WITH Casted AS
(
SELECT ID
,CAST(YourXML AS NVARCHAR(MAX)) AS TheXmlAsString
FROM #yourTbl
)
,WithPosition AS
(
SELECT Casted.*
,CHARINDEX(N'<my:PositionDepartment>',TheXmlAsString) + LEN(N'<my:PositionDepartment>') AS FirstLetter
FROM Casted
)
SELECT ID
,SUBSTRING(TheXmlAsString,FirstLetter,CHARINDEX('<',TheXmlAsString,FirstLetter)-FirstLetter)
FROM WithPosition
UPDATE 2
According to your edit the following returns a NULL value. This is good, because it shows, that the cast was successfull.
SELECT
CAST(
REPLACE(CAST(de.TRIGGERS_XML_DATA AS VARCHAR(MAX)), 'encoding="utf-16"', '')
AS XML).value('(triggers/triggerDefinition/config/item/value)[1]',
'NVARCHAR(max)') as Item, de.ENVIRONMENT_ID
from dbo.DEPLOYMENT_ENVIRONMENT as de
where de.ENVIRONMENT_ID = 19234819
Try this (skip namespace with wildcard):
SELECT
CAST(
REPLACE(CAST(de.TRIGGERS_XML_DATA AS VARCHAR(MAX)), 'encoding="utf-16"', '')
AS XML).value('(*:triggers/*:triggerDefinition/*:config/*:item/*:value)[1]', 'NVARCHAR(max)') as Item, de.ENVIRONMENT_ID
from dbo.DEPLOYMENT_ENVIRONMENT as de
where de.ENVIRONMENT_ID = 19234819
And this should be even better:
SELECT
CAST(CAST(de.TRIGGERS_XML_DATA AS NVARCHAR(MAX)) AS XML).value('(*:triggers/*:triggerDefinition/*:config/*:item/*:value)[1]', 'NVARCHAR(max)') as Item, de.ENVIRONMENT_ID
from dbo.DEPLOYMENT_ENVIRONMENT as de
where de.ENVIRONMENT_ID = 19234819
UPDATE 3
I'd rather cut away the full declaration. Your posted example would go like this
DECLARE #DEPLOYMENT_ENVIRONMENT TABLE(ENVIRONMENT_ID INT, TRIGGERS_XML_DATA NTEXT);
INSERT INTO #DEPLOYMENT_ENVIRONMENT VALUES
(19234819,N'<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configuration xml:space="preserve">
<triggers>
<defined>true</defined>
<triggerDefinition>
<id>1</id>
<name>After successful deployment</name>
<userDescription/>
<isEnabled>true</isEnabled>
<pluginKey>com.atlassian.bamboo.triggers.atlassian-bamboo-triggers:afterSuccessfulDeployment</pluginKey>
<triggeringRepositories/>
<config>
<item>
<key>deployment.trigger.afterSuccessfulDeployment.triggeringEnvironmentId</key>
<value>19234819</value>
</item>
</config>
</triggerDefinition>
</triggers>
<bambooDelimiterParsingDisabled>true</bambooDelimiterParsingDisabled>
</configuration>');
WITH Casted AS
(
SELECT CAST(de.TRIGGERS_XML_DATA AS NVARCHAR(MAX)) AS XmlAsSting
FROM #DEPLOYMENT_ENVIRONMENT as de
where de.ENVIRONMENT_ID = 19234819
)
SELECT CAST(SUBSTRING(XmlAsSting,CHARINDEX('?>',XmlAsSting)+2,8000) AS XML).value('(/*:configuration/*:triggers/*:triggerDefinition/*:config/*:item/*:value)[1]', 'NVARCHAR(max)') as Item
FROM Casted;

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

How to Split a String XML Column into no of rows based on the nodes present inside the XML Field

I have a SQL table in the below format with only one XML column:
[XML]
<?xml version="1.0" encoding="UTF-8"?>
<htmlString>
<MENUACTION v="Contact"/>
<MENUKEY/>
<PROGRAM v="SOWMENU03"/>
<HTMLFILE v="CGI/TEMP"/>
</htmlString>
I want to split this table row as follows based on the nodes inside
[Value_XML] [Control_ID] [Data_Value]**
<MENUACTION v="Contact"/> MENUACTION Contact
<MENUKEY/> MENUKEY
<PROGRAM v="SOWMENU03"/> Program SOWMENU03
<HTMLFILE v="CGI/TEMP"/> HTMLFILE CGI/TEMP
Note: The attribute value is mentioned using V=.Some times V will not be available for a node like MENUKEY tag.
Try this (you can replace the NULL value with empty string if you want):
DECLARE #DataSource TABLE
(
[Data] XML
);
INSERT INTO #DataSource ([Data])
SELECT '<?xml version="1.0" encoding="UTF-8"?>
<htmlString>
<MENUACTION v="Contact"/>
<MENUKEY/>
<PROGRAM v="SOWMENU03"/>
<HTMLFILE v="CGI/TEMP"/>
</htmlString>'
SELECT T.c.query('.') AS [Value_XML]
,T.c.value('local-name(.)', 'NVARCHAR(MAX)') AS [Control_ID]
,T.c.value('(./#v)[1]', 'varchar(50)') AS [Data_Value]
FROM #DataSource DS
CROSS APPLY [Data].nodes('/htmlString/*') AS T(c)