Replace partial data in an element using xquery - replace

I am trying to replace partial data of an element with empty string in xquery. I have ticket details xml and email to remove xml with me. Now i need to form an xquery which would modify the ticket details xml by removing the email ids that are there in the element EmailIdToDelete available in second xml from the element EmailId available in first xml and give me the expected output. The xquery version used is not latest. I guess it is very old. Need to check that.
Could someone please help me with this?
Ticket Details xml:
<TicketDetails>
<EmailInformation>
<EmailId>test1#aaa.com;test2#aaa.com;test3#aaa.com;test4#aaa.com;test5#aaa.com;test6#aaa.com;test7#aaa.com;test8#aaa.com</EmailId>
</EmailInformation>
</TicketDetails>
Email to remove xml:
<Details>
<EmailDetails>
<EmailIdToDelete>test2#aaa.com</EmailIdToDelete>
<EmailIdToDelete>test5#aaa.com</EmailIdToDelete>
<EmailIdToDelete>test8#aaa.com</EmailIdToDelete>
</EmailDetails>
</Details>
Expected output:
<TicketDetails>
<EmailInformation>
<EmailId>test1#aaa.com;test3#aaa.com;test4#aaa.com;test6#aaa.com;test7#aaa.com;</EmailId>
</EmailInformation>
</TicketDetails>

You could tokenize on ; and then simply compare and only select the values without a match in the second document:
<EmailId>{string-join(tokenize(TicketDetails/EmailInformation/EmailId, ';')[not(. = $remove/Details/EmailDetails/EmailIdToDelete)], ';')}</EmailId>
where $remove is the second document e.g. let $remove := doc('remove.xml') or an external parameter:
declare variable $remove as document-node() external := document {
<Details>
<EmailDetails>
<EmailIdToDelete>test2#example.com</EmailIdToDelete>
<EmailIdToDelete>test5#example.com</EmailIdToDelete>
<EmailIdToDelete>test8#example.com</EmailIdToDelete>
</EmailDetails>
</Details>
};
<TicketDetails>
<EmailInformation>
<EmailId>{string-join(tokenize(TicketDetails/EmailInformation/EmailId, ';')[not(. = $remove/Details/EmailDetails/EmailIdToDelete)], ';')}</EmailId>
</EmailInformation>
</TicketDetails>
https://xqueryfiddle.liberty-development.net/gWmuPs2

Related

xquery/xslt for selecting a value based on a specific value

I am stuck on an xquery for below request. I want to select the address based on the type value.
i.e. if type is "StreetAddress" then pick freeFormat "Maguire"
<associatedAddress>
<address xmlns="http://services.oracle.com/v1.0/Common">
<type xmlns="http://services.oracle.com/v1.0/Common">StreetAddress</type>
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">Maguire</freeFormat>
</address>
<address>
<type xmlns="http://services.oracle.com/v1.0/Common">CityAddress</type>
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">SanFransisco</freeFormat>
</address>
</associatedAddress>
I tried in couple of ways:
if associatedAddress/address/type="StreetAddress"
then.., But this one gives me only the first address
I also tried to use a for loop and then use if case inside it but even that gave me just the first address
Please let me know any other options. Thanks
The XPath for fetching value Maguire using the value in <type> is
<xsl:value-of select="associatedAddress/address[type='StreetAddress']/freeFormat" />
You need to take care of the namespace associated with the different elements in your input XML when fetching the value. If the namespace is not properly handled in the XSL, the value will not be extracted.
Since you asked for xQuery given your input xml, this:
declare namespace common="http://services.oracle.com/v1.0/Common";
for $n in associatedAddress//common:type[. = "StreetAddress"]
return
$n/../common:freeFormat
produces the desired result
<freeFormat xmlns="http://services.oracle.com/v1.0/Common">Maguire</freeFormat>
The trick is that in your input xml associatedAddress is not in the same namespace as the other elements, so you need to adjust your query to take this into account.
You mention an if clause, but its is not clear what the desired output is from your question. You can use the below as a template to modify results.
declare namespace common="http://services.oracle.com/v1.0/Common";
for $n in associatedAddress//common:type
return
switch($n)
case "StreetAddress" return $n/../common:freeFormat
case "CityAddress" return "something else"
default
return ()

How to use contains() with a set of strings in XSLT

I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.

BaseX XQuery replace

I have the following problem. I want to replace a value of an element in my xquery-file by using baseX as the database. The xquery code is as follows:
let $db := doc('update.xml')
replace value of node $db//elem with 'haha'
return <result> {$db//elem/text()} </result>
The xml document contains the following elements:
<?xml version="1.0" encoding="ISO-8859-1"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<check>
<ok>
<elem>test</elem>
<help></help>
</ok>
</check>
</root>
Everytime I want to execute this xquery an error like this is thrown:
Expecting 'where', 'order' or 'return' expression
so what should i do or change, to just replace the text "test" by "haha" in the element ?
If I use just this line of code it works, but I have to read out of URL-Parameter so I need more lines of code, except the "replace...." line!
let starts a flwor-expression which may not directly contain update statements. You will have to put a return between these two:
let $db := doc('update.xml')
return
replace value of node $db//elem with 'haha'
You will also be able to do arbitrary calculations, but make sure not to have any output returned by your query.
There is no way to use updating statements and return a result at the same time.

xsl - select node by child

I have a problem selecting just elements of an xml, which contain a specific child node. Asumme the following part of an xml:
<root>
<Navision.Buchungen>
<Saldo>-110867.7500</Saldo>
<Navision.Kontostruktur>
<Bereich>1</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
<Navision.Buchungen>
<Saldo>-3082585.2100</Saldo>
<Navision.Kontostruktur>
<Bereich>2</Bereich>
</Navision.Kontostruktur>
</Navision.Buchungen>
...
</root>
Now I have an xsl part like this to get the sum of 'Saldo':
<xsl:variable name="FACT0" select="sum(//root/Navision.Buchungen/Saldo)"/>
But how can I select just the Saldo for 'Bereich' 1 for example?
Use this XPath:
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
//root/Navision.Buchungen[Navision.Kontostruktur/Bereich = 1]/Saldo
Edited:
oh already posted.
For further problems you can use one of the online testbeds like this one. And of course good manuals like those from w3schools, also with testbeds for xsl

How do acquire a xml string for a child using msxml4?

Using MSXML4, I am creating and saving an xml file:
MSXML2::IXMLDOMDocument2Ptr m_pXmlDoc;
//add some elements with data
SaveToDisk(static_cast<std::string>(m_pXmlDoc->xml));
I now need to acquire a substring from m_pXmlDoc->xml and save it. For example, if the full xml is:
<data>
<child1>
<A>data</A>
<One>data</One>
<B>data</B>
</child1>
</data>
I want to store this substring instead:
<A>data</A>
<One>data</One>
<B>data</B>
How do I get this substring using MXML4?
Use XPath queries. See the MSDN documentaion for querying nodes. Basically you need to call the selectNodes API with the appropriate XPath expression that matches the part of the DOM you are interested in.
// Query a node-set.
MSXML4::IXMLDOMNodeListPtr pnl = pXMLDom->selectNodes(L"//child/*");