Exclude a grandchild from XML using XPath - sql-server

Trying to exclude a specific grandchild when reading an XML structure using SQL Server 2017.
I have an example structure as follows:
<root>
<a id="parent">
<b id = "child1">
<c id = "grandchild1"/>
<c id = "grandchild2"/>
</b>
<b id = "child2">
<c id = "grandchild3"/>
<c id = "grandchild4"/>
</b>
</a>
</root>
I can exclude a specific child using c[not(contains(#id,"grandchild3"))] when I navigate to for example the node <b id = "child2">.
But how would I exclude that specific grandchild when processing the rest of the XML from the root node?
What i would like to receive, so I can write it to a file, is a structure like this:
<root>
<a id="parent">
<b id = "child1">
<c id = "grandchild1"/>
<c id = "grandchild2"/>
</b>
<b id = "child2">
<c id = "grandchild4"/>
</b>
</a>
</root>
As can be seen grandchild3 is now missing from this structure.

Doing this in a SELECT is surprisingly involved, but fortunately you don't need that. You can make a copy of your xml, modify it with .modify, then use that:
declare #original xml = '<root>
<a id="parent">
<b id = "child1">
<c id = "grandchild1"/>
<c id = "grandchild2"/>
</b>
<b id = "child2">
<c id = "grandchild3"/>
<c id = "grandchild4"/>
</b>
</a>
</root>'
declare #new xml = #original
set #new.modify('delete //c[#id="grandchild3"]')
select #new
gives
<root>
<a id="parent">
<b id="child1">
<c id="grandchild1" />
<c id="grandchild2" />
</b>
<b id="child2">
<c id="grandchild4" />
</b>
</a>
</root>
as desired.
The xpath //c[#id="grandchild3"] means "Any c node (anywhere in the document) with an id attribute with value grandchild3".

Related

Basex Xquery filter retrieved data

<a id="aId" deleted="false">
<b id="bIdOne" deleted="false">
<c>cValue</c>
<d id="dId" deleted="true">
<e>eValue</e>
</d>
<f id="fId" deleted="false">
<g>gValue</g>
</f>
</b>
<b id="bIdTwo" deleted="true">
<c>cValue</c>
<d id="dId" deleted="true">
<e>eValue</e>
</d>
<f id="fId" deleted="false">
<g>gValue</g>
</f>
</b>
<b id="bIdThree" deleted="false">
<c>cValue</c>
<d id="dId" deleted="true">
<e>eValue</e>
</d>
<f id="fId" deleted="true">
<g>gValue</g>
</f>
</b>
<Y><yValue</Y>
</a>
Expected Output :
retrieve list of b and its child where deleted attribute is false, Expected output is below
query should be something like :
for $x in $data//descendant::[(#deleted=\"false\" or empty(#deleted))] return $x/b";
<b id="bIdOne" deleted="false">
<c>cValue</c>
<f id="fId" deleted="false">
<g>gValue</g>
</f>
</b>
<b id="bIdThree" deleted="false">
<c>cValue</c>
<Y><yValue</Y>
</b>
You are probably looking for something like this:
let $data := [your code above, fixed - your <Y> is in the wrong place for your output]
return
element root
{
for $e1 in $data//b[#deleted="false"]
let $dat :=$e1//*[not(#deleted="true")][not(ancestor::*[#deleted="true"])]
return
element{name($e1)}{$e1/#*,$dat}
}
Output should be your desired output.

FormattedHtmlMessage react-intl v4

After upgrading to react-intl#4, we are facing a problem with all formattedHtmlMessages which have been used as html tag.
<FormattedHTMLMessage id={`${page}.legalNotice`} />
We tried replacing by formattedMessage and tried values={{...}}
<FormattedMessage id={`${page}.legalNotice`} />
But first of all the html is not considered as html tags.
Also as we use dynamic translation with different and undefined number of 'href's or even different 'target's on tags(links) it does not seem to be the solution.
Our translations are something similar to this:
"myPage1.legalNotice" : "By clicking here I accept <a target="_self" title="my specific title" href='first_link.pdf'>LINK1</a>, and <a target="_blank" title="second_title" href='second_link'>LINK2</a> and <a target="_blank" href='third_link' title='third_title'>LINK3</a>."
"myPage2.legalNotice" : "Another Text .. <a target="_blank" href='another_link.pdf'>LINK2</a>."
Check the Migration guide.
https://formatjs.io/docs/react-intl/upgrade-guide-3x
You have to provide the description for all the tags you use inside the message.
<FormattedMessage
defaultMessage="To buy a shoe, <a>visit our website</a> and <cta>eat a shoe</cta>"
values={{
a: msg => (
<a class="external_link" target="_blank" href="https://www.shoe.com/">
{msg}
</a>
),
cta: msg => <strong class="important">{msg}</strong>,
}}
/>
Update: If you need to pass custom variables inside the variable
Assuming you have customHref or customTarget variables defined:
a: msg => (
<a class="external_link" target="${customTarget}" href="${customHref}">
{msg}
</a>
)
To keep it the old way you can use such hack:
let message = intl.formatMessage({ id });
if (values) {
Object.entries(values).forEach(([key, value]) => {
message = message.replace(`{${key}}`, value.toString());
});
}
return(
<span
dangerouslySetInnerHTML={{ __html: formattedMessage }}
/>
)

T-SQL Find node with matching text and extract value from sibling node in XML field

I've a table with an xml field having content like this:
<ContentBlock xmlns="http://corp.com/wdpr/ContentBlock">
<key xlink:href="tcm:0-133036" xlink:title="entityCard" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<template xlink:href="tcm:0-132970" xlink:title="card-header-read-more-all-media" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<section>
<name xlink:href="tcm:0-132988" xlink:title="header" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Make a Friend</p>
</content>
</section>
<section>
<name xlink:href="tcm:0-133110" xlink:title="readMore" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals at the only petting zoo in Disney’s Animal
Kingdom park.
</p>
</content>
</section>
<section>
<name xlink:href="tcm:0-132939" xlink:title="readAll" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals.
</p>
<p xmlns="http://www.w3.org/1999/xhtml">Pet, brush and feed domesticated creatures
</p>
<ul xmlns="http://www.w3.org/1999/xhtml">
<li xmlns="http://www.w3.org/1999/xhtml">Goats</li>
</ul>
<p xmlns="http://www.w3.org/1999/xhtml">Handy animal brushes are available .
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
<strong xmlns="http://www.w3.org/1999/xhtml">Keeping Clean</strong>
<br xmlns="http://www.w3.org/1999/xhtml"/>Guests are encouraged to cleanse.</p>
</content>
</section>
<media>
<name xlink:href="tcm:0-201994" xlink:title="media" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<key xlink:href="tcm:0-132952" xlink:title="170 x 96" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<image xlink:href="tcm:0-231377" xlink:title="section-01.jpg" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</media>
</ContentBlock>
My goal is to have a query where I can find (for example) the <p> that contains the text "Make a Friend", then get the xlink:href of the <name> element in the same <section> tag.
I tried a few options like in this posts: here and here but I'm not getting the results I need.
This doesn't work
SELECT a.value1,
x.XmlCol.value('(section/content/p)[1]','VARCHAR(100)') AS SendMethod
FROM #test a
CROSS APPLY a.AppConfig.nodes('/ContentBlock') x(XmlCol)
WHERE x.XmlCol.exist('section/content/p[contains(.,"Make a Friend")]') = 1
How can I get it?
Thanks in advance. Guillermo.
Try it like this:
DECLARE #mockup TABLE(ID INT IDENTITY,YourXML XML);
INSERT INTO #mockup VALUES
('<ContentBlock xmlns="http://corp.com/wdpr/ContentBlock">
<key xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-133036" xlink:title="entityCard" xlink:type="simple" />
<template xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-132970" xlink:title="card-header-read-more-all-media" xlink:type="simple" />
<section>
<name xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-132988" xlink:title="header" xlink:type="simple" />
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Make a Friend</p>
</content>
</section>
<section>
<name xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-133110" xlink:title="readMore" xlink:type="simple" />
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals at the only petting zoo in Disney’s Animal
Kingdom park.
</p>
</content>
</section>
<section>
<name xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-132939" xlink:title="readAll" xlink:type="simple" />
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals.
</p>
<p xmlns="http://www.w3.org/1999/xhtml">Pet, brush and feed domesticated creatures
</p>
<ul xmlns="http://www.w3.org/1999/xhtml">
<li xmlns="http://www.w3.org/1999/xhtml">Goats</li>
</ul>
<p xmlns="http://www.w3.org/1999/xhtml">Handy animal brushes are available .
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
<strong xmlns="http://www.w3.org/1999/xhtml">Keeping Clean</strong>
<br xmlns="http://www.w3.org/1999/xhtml" />Guests are encouraged to cleanse.</p>
</content>
</section>
<media>
<name xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-201994" xlink:title="media" xlink:type="simple" />
<key xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-132952" xlink:title="170 x 96" xlink:type="simple" />
<image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="tcm:0-231377" xlink:title="section-01.jpg" xlink:type="simple" />
</media>
</ContentBlock>');
-First I declare a variable to make the search-string dynamic:
DECLARE #SearchFor VARCHAR(100)='Make a Friend';
--Your XML has a default namespace in the outermost element.
--What makes things a bit weird: There is another default namespace on the element <p>
--We can declare this with a speaking prefix
WITH XMLNAMESPACES(DEFAULT 'http://corp.com/wdpr/ContentBlock'
,'http://www.w3.org/1999/xlink' AS xlink
,'http://www.w3.org/1999/xhtml' AS InnerDflt )
SELECT YourXML.value('(/ContentBlock/section[(content/InnerDflt:p/text())[1]=sql:variable("#SearchFor")]/name/#xlink:href)[1]','nvarchar(max)')
FROM #mockup
The query runs as this:
Start with <ContentBlock>. Look for a <section>, where the text() of <p> below <content> is the search-string. Important: At this stage we are still on the level of <section>. So we can continue the XPath with <name> and find the attribute there.
Shnugo stole my thunder but I'm still posting what I put together as it will work as well and demonstrates a few tricks (e.g. *: for when you're too lazy to add the correct namespace syntax) ;). I was going to mention the use of sql:variable to pass a SQL variable into your XPath expressions - shnugo's post demonstrates how to do that (it's missing in what I'm posting).
-- bonus sample data
DECLARE #xml XML =
'<ContentBlock xmlns="http://corp.com/wdpr/ContentBlock">
<key xlink:href="tcm:0-133036" xlink:title="entityCard" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<template xlink:href="tcm:0-132970" xlink:title="card-header-read-more-all-media" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<section>
<name xlink:href="tcm:0-132988" xlink:title="header" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Make a Friend</p>
</content>
</section>
<section>
<name xlink:href="tcm:0-133110" xlink:title="readMore" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals at the only petting zoo in Disney’s Animal
Kingdom park.
</p>
</content>
</section>
<section>
<name xlink:href="tcm:0-132939" xlink:title="readAll" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<content>
<p xmlns="http://www.w3.org/1999/xhtml">Meet and greet friendly animals.</p>
<p xmlns="http://www.w3.org/1999/xhtml">Pet, brush and feed domesticated creatures
</p>
<ul xmlns="http://www.w3.org/1999/xhtml">
<li xmlns="http://www.w3.org/1999/xhtml">Goats</li>
</ul>
<p xmlns="http://www.w3.org/1999/xhtml">Handy animal brushes are available .
</p>
<p xmlns="http://www.w3.org/1999/xhtml">
<strong xmlns="http://www.w3.org/1999/xhtml">Keeping Clean</strong>
<br xmlns="http://www.w3.org/1999/xhtml"/>Guests are encouraged to cleanse.</p>
</content>
</section>
<media>
<name xlink:href="tcm:0-201994" xlink:title="media" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<key xlink:href="tcm:0-132952" xlink:title="170 x 96" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
<image xlink:href="tcm:0-231377" xlink:title="section-01.jpg" xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink"/>
</media>
</ContentBlock>';
DECLARE #test TABLE (someId INT IDENTITY, AppConfig XML);
INSERT #test (AppConfig) VALUES (#xml),(CAST(REPLACE(REPLACE(CAST(#xml AS VARCHAR(8000)),
'Make a Friend','xxx'),'Keeping Clean','Make a Friend') AS XML));
-- SOLUTION
WITH XMLNAMESPACES ('http://www.w3.org/1999/xlink' AS xlink)
SELECT t.someId, href = cb.x.value('(../*:name/#xlink:href)[1]', 'varchar(8000)')
FROM #test AS t
CROSS APPLY t.AppConfig.nodes('*:ContentBlock/*:section/*:content') AS cb(x)
WHERE cb.x.exist('*:p[contains(.,"Make a Friend")]') = 1;
Returns:
someId href
----------- -------------
1 tcm:0-132988
2 tcm:0-132939

How do you get a node that contains a child node with a given attribute value in an XML document using sql server?

I'm working on parsing XML documents using SQL Server 2008. I'm a complete noob and I was wondering if I can get help from you guys.
I have an XML document like the one below and I want to get the "section" node where the "code" node has val=5.
<root>
<section>
<code val=6 />
...
</section>
<section>
<code val=5 />
...
</section>
<section>
<code val=4 />
...
</section>
</root>
So the result should be:
<section>
<code val=5 />
...
</section>
I tried doing this, but it didn't work:
select #xml.query('/root/section')
where #xml.value('/root/section/code/#val,'int')= '5'
I also tried this:
select #xml.query('/root/section')
where #xml.exist('/root[1]/section[1]/code[#val="1"])= '1'
Any ideas? Thanks in advance.
You could use this query:
DECLARE #x XML=N'
<root>
<section atr="A">
<code val="5" />
</section>
<section atr="B">
<code val="6" />
</section>
<section atr="C">
<code val="5" />
</section>
</root>';
SELECT a.b.query('.') AS SectionAsXmlElement,
a.b.value('#atr','NVARCHAR(50)') AS SectionAtr
FROM #x.nodes('/root/section[code/#val="5"]') a(b);
Results:
SectionAsXmlElement SectionAtr
------------------------------------------- ----------
<section atr="A"><code val="5" /></section> A
<section atr="C"><code val="5" /></section> C
Instead of where you can apply the constraint in an XPath predicate:
#xml.query('/root/section[code/#val=5]')

SQL XML Insert multiple elements

Please help! I have a field of xml within a database. I am trying to insert a new element for each element in the field. E.g:
<some-element>
<a> </a>
<b> </b>
</some-element>
<some-element>
<a> </a>
<b> </b>
</some-element>
would insert element c and go to:
<some-element>
<a> </a>
<b> </b>
<c> </c>
</some-element>
<some-element>
<a> </a>
<b> </b>
<c> </c>
</some-element>
I know I can use #fieldname.modify('insert #CElement into (/some-element)[1]') but this only changes the first element! I want to repeat this for all elements.
Any help would be very much appreciated! (I am using SQL server 2008)
Moved from answer:
Thank you for your reply! Perhaps it would help if I was more specific... I am describing a simple table of data within the xml. I am trying to add a new column. I can add the column within the column descriptions but I need to add the column element to all the rows. (It would not be a simple task to change the structure and I would like to avoid this!) E.g:
<Table>
<Columns>
<Column ID="0">
<Column-Name>0NAME</Column-Name>
</Column>
<Column ID="1">
<Column-Name>1NAME</Column-Name>
</Column>
<Column ID="2">
<Column-Name>2NAME</Column-Name>
</Column>
<Column ID="3">
<Column-Name>!!!! THIS COLUMN IS BEING ADDED !!!!!</Column-Name>
</Column>
</Columns>
<Rows>
<Row ID="0">
<C ID="0">0 contents here</C>
<C ID="1">0 contents here</C>
<C ID="2">0 contents here</C>
<!-- NEW COLUMN NEEDS TO BE CREATED HERE -->
</Row>
<Row ID="1">
<C ID="0">1 contents here</C>
<C ID="1">1 contents here</C>
<C ID="2">1 contents here</C>
<!-- NEW COLUMN NEEDS TO BE CREATED HERE -->
</Row>
<Row ID="2">
<C ID="0">2 contents here</C>
<C ID="1">2 contents here</C>
<C ID="2">2 contents here</C>
<!-- NEW COLUMN NEEDS TO BE CREATED HERE -->
</Row>
</Rows>
</Table>
You can recreate your XML, e.g.:
declare #x xml = '<some-element>
<a>1</a>
<b>2</b>
</some-element>
<some-element>
<a>3</a>
<b>4</b>
</some-element>'
select #x.query
('
for $e in some-element
return
<some-element>
{ $e/a }
{ $e/b }
<c/>
</some-element>
')
Adapting the answer provided by Kirill Polishchuk to the new XML structure:
select #XML.query
('
for $c in Table/Columns
return
<Table>
<Columns>
{ $c/* }
</Columns>
<Rows>
{
for $r in Table/Rows/Row
return
<Row>
{ $r/* }
<C ID="3"/>
</Row>
}
</Rows>
</Table>
')

Resources