XQuery Match assertion on SOAPUI - xquery

I have a soap testStep in SOAPUI with an XQuery match.
The XML (simplified) look as follows:
<root>
<element>
<a>a</a>
<b>b</b>
<c>c</c>
<d>d</d>
</element>
</root>
I want to make an XQuery to get all child nodes from <element> removing a child element depending on his node name. My XQuery looks like:
for $x in //root/element/element()
return
if (name($x) != 'a') then $x
else ""
I expect the next result:
<b>b</b>
<c>c</c>
<d>d</d>
I think that my XQuery is correct, I tested with an XQuery online evaluator and looks ok, you can try with the follow link
However when I use this expression in a XQuery Match assertion in SOAPUI I get the following message: More than one match in current response. How can achieve this with SOAPUI?
Thanks,

Doing some tries finally I found the solution, the way to do this XQuery in SOAPUI is specifying a root node in the XQuery expression i.e:
<MyResult>
{
for $x in //root/element/element()
return
if (name($x) != 'a') then $x
else ""
}
</MyResult>

Related

recursive replacement

How to find all the empty string and replace it with 'empty' element in xquery without using xquery updating. I achieved this using xquery update but I want to get without using update.
This kind of transformation is easier in XSLT. In XSLT 3.0 you could write:
<xsl:transform version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="*[.='']"><empty/></xsl:template>
</xsl:transform>
The reason this works is that "on-no-match='shallow-copy'" causes a recursive processing of the input tree, where if there is no explicit template rule for a particular element, the processor just copies the element and recurses down to process its children.
XQuery doesn't have a built-in primitive to do this, but you can achieve the same thing by writing it out explicitly. You need a recursive function:
declare function local:process($e as node()) as node() {
if ($e[self::element()])
then
if ($e='')
then <empty/>
else
element {node-name($e)} {
$e/#*,
$e/child::node()/local:process(.)
}
else (:text, comment, PI nodes:)
$e
};
local:process($in/*)

XQuery: How to output string literal & in value of attribute

test.xqy:
element test
{
attribute haha {"&"}
}
command line:
$ basex test.xqy
<test haha="&"/>
And I need the output to be: <test haha="&"/>. The XML entity is not processed by BaseX?
The document <test haha="&"/> is not well-formed, so there’s no chance to get this output with BaseX or any other XML processor. However, the value of haha is indeed '&'; you will see this if you return the attribute value as string:
string(<test haha="&"/>/#haha)

How to tidy-up Processing Instructions in Marklogic

I have a content which is neither a valid HTML nor a XML in my legacy database. Considering the fact, it would be difficult to clean the legacy, I want to tidy this up in MarkLogic using xdmp:tidy. I am currently using ML-8.
<sub>
<p>
<???†?>
</p>
</sub>
I'm passing this content to tidy functionality in a way :
declare variable $xml as node() :=
<content>
<![CDATA[<p><???†?></p>]]>
</content>;
xdmp:tidy(xdmp:quote($xml//text()),
<options xmlns="xdmp:tidy">
<assume-xml-procins>yes</assume-xml-procins>
<quiet>yes</quiet>
<tidy-mark>no</tidy-mark>
<enclose-text>yes</enclose-text>
<indent>yes</indent>
</options>)
As a result it returns :
<p>
<? ?†?>
</p>
Now this result is not the valid xml format (I checked it via XML validator) due to which when I try to insert this XML into the MarkLogic it throws an error saying 'MALFORMED BODY | Invalid Processing Instruction names'.
I did some investigation around PIs but not much luck. I could have tried saving the content without PI but this is also not a valid PI too.
That is because what you think is a PI is in fact not a PI.
From W3C:
2.6 Processing Instructions
[Definition: Processing instructions (PIs) allow documents to contain
instructions for applications.]
Processing Instructions
[16] PI ::= '' Char*)))?
'?>'
[17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' |
'l'))
So the PI name cannot start with ? as in your sample ??†
You probably want to clean up the content before you pass it to tidy.
Like below:
declare variable $xml as node() :=
<content><![CDATA[<p>Hello <???†?>world</p>]]></content>;
declare function local:copy($input as item()*) as item()* {
for $node in $input
return
typeswitch($node)
case text()
return fn:replace($node,"<\?[^>]+\?>","")
case element()
return
element {name($node)} {
(: output each attribute in this element :)
for $att in $node/#*
return
attribute {name($att)} {$att}
,
(: output all the sub-elements of this element recursively :)
for $child in $node
return local:copy($child/node())
}
(: otherwise pass it through. Used for text(), comments, and PIs :)
default return $node
};
xdmp:tidy(local:copy($xml),
<options xmlns="xdmp:tidy">
<assume-xml-procins>no</assume-xml-procins>
<quiet>yes</quiet>
<tidy-mark>no</tidy-mark>
<enclose-text>yes</enclose-text>
<indent>yes</indent>
</options>)
This would do the trick to get rid of all PIs (real and fake PIs)
Regards,
Peter

XQuery fn:deep-equal - comparing text from XPath with string literal

I'm trying to use XQuery function fn:deep-equal to compare sections of XML documents and I'm getting unexpected behaviour. When comparing XPath value with string literal, function returns false.
For example following code
let $doc :=
<root>
<child><message>Hello</message></child>
</root>
let $message := <message>Hello</message>
let $value := $doc/child/message/text()
let $compareDirectly := fn:deep-equal($value, "Hello") (: -> false :)
let $compareAsString := fn:deep-equal(fn:concat($value, ""), "Hello") (: -> true :)
let $comparePath := fn:deep-equal($value, $message/text()) (: -> true :)
return
<results>
<value>{$value}</value>
<directly>{$compareDirectly}</directly>
<asString>{$compareAsString}</asString>
<path>{$comparePath}</path>
</results>
Executed using Saxon, XQuery program generates following XML
<?xml version="1.0" encoding="UTF-8"?>
<results>
<value>Hello</value>
<directly>false</directly>
<asString>true</asString>
<path>true</path>
</results>
I'd expect $compareDirectly to be true (same as two other examples), but fn:deep-equal does not seem to work as I would intuitively expect. I'm wondering whether this is correct behaviour.
Is there any better wah how to compare two XML nodes?
I'm lookig for some generic solution which could be used for both XML snippets (like values of $doc or $message in example) and also for this special case with string literal.
From the spec:
To be deep-equal, they must contain items that are pairwise deep-equal; and for two items to be deep-equal, they must either be atomic values that compare equal, or nodes of the same kind, with the same name, whose children are deep-equal.
So this is why it doesn't return true when comparing a text node to an atomic type. In your other two examples you are comparing 2 string atomic types. It looks as if you don't need deep-equal, which compares nodes recursively. If that's the case, then you can just compare the strings:
$doc/child/message/string() eq $message/string()
=> true()
If there are other requirements, then you may need to update your example to demonstrate those more clearly.

How to indicate 'missing' tags in XQuery?

I have an XML file:
$xml := <xml>
<element>
<text>blahblah</text>
</element>
<element>
</element>
<element>
<text>blahblah</text>
</element>
</xml>
I can use the query
for $x in $xml/xml/element/text return string($x)
This gives me a list
blahblah
blahblah
with no indication that there is an element which has no element. What I'd like to do is use a query which, if there is no such element, returns, say "missing". How do I do this?
For a sequence of strings (slightly modified version of the first answer):
for $e in $xml/xml/element
return
if ($e/text)
then string($e/text)
else "missing"
or using a let (which seems a little cleaner to me... but it's probably just 6 of one and half dozen of the other):
for $e in $xml/xml/element
let $text := string($e/text)
return
if ($text)
then $text
else "missing"
Hope that helps.
Are you trying to return the "element" elements that don't have any children? (In your example, it's the second occurrence of "element" as the first and last contain "text" elements.)
If so, you can use a predicate in an XPath expression:
/xml/element[not(*)]
This should work:
for $x in $xml/xml/element
return
if (text)
then string(text)
else "missing"
in MarkLogic
for $e in $xml/xml/element
return ($e/text,"missing")
$xml/element/string((text,"missing")[1])
functions are allowed in XPath expressions, so an explicit loop is not needed here.
the expression (text,"missing")[1]
returns the first non-null item in the sequence of the text element followed by the string "missing"
you can use the eXist sandbox to execute code snippets:-
http://demo.exist-db.org/exist/sandbox/sandbox.xql

Resources