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

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)

Related

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 Match assertion on SOAPUI

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>

how to change the XML structure using XQuery

I have a XML file containing Employees Name and the Job done by them.
The structure of the XML file is -
<Employee>AAA#A#B#C#D</Employee>
<Employee>BBB#A#B#C#D</Employee>
<Employee>CCC#A#B#C#D</Employee>
<Employee>DDD#A#B#C#D</Employee>
There are thousands of records and I have to change structure to -
<Employee>
<Name>AAA</Name>
<Jobs>
<Job>A</Job>
<Job>B</Job>
<Job>C</Job>
<Job>D</Job>
</Jobs>
</Employee>
How to get this done using XQuery in BaseX ?
3 XQuery functions, substring-before, substring-after and tokenize are used to get
the required output.
substring-before is used to get the Name.
Similarly, the substring-after is used to get the Job portion.
Then the tokenize function, is used to split the Jobs.
let $data :=
<E>
<Employee>AAA#A#B#C#D</Employee>
<Employee>BBB#A#B#C#D</Employee>
<Employee>CCC#A#B#C#D</Employee>
<Employee>DDD#A#B#C#D</Employee>
</E>
for $x in $data/Employee
return
<Employee>
{<Name>{substring-before($x,"#")}</Name>}
{<Jobs>{
for $tag in tokenize(substring-after($x,"#"),'#')
return
<Job>{$tag}</Job>
}</Jobs>
}</Employee>
HTH...
Tokenizing the string is probably easier and faster. tokenize($string, $pattern) splits $string using the regular expression $pattern, head($seq) returns the first value of a sequence and tail($seq) all but the first. You could also use positional predicates of course, but these functions are easier to read.
for $employee in //Employee
let $tokens := tokenize($employee, '[##]')
return element Employee {
element Name { head($tokens) },
element Jobs {
for $job in tail($tokens)
return element Job { $job }
}
}

To remove the node but keep the value inside intact through XQuery

I have a content.xml modelled as below
<root>
<childnode>
Some text here
</childnode>
</root>
I am trying to remove the <childnode> and update the content.xml with only the value of it
so the output looks like
<root>
Some Text here
</root>
I wrote a function to perform this but anytime I run it it gives me error as "unexpected token: modify". I was thinking of a way to accomplish this without using functx functions.
xquery version "1.0";
declare namespace request="http://exist-db.org/xquery/request";
declare namespace file="http://exist-db.org/xquery/file";
declare namespace system="http://exist-db.org/xquery/system";
declare namespace util="http://exist-db.org/xquery/util";
declare namespace response="http://exist-db.org/xquery/response";
declare function local:contentUpdate() {
let $root := collection('/lib/repository/content')//root/childNode
let $rmChild := for $child in $root
modify
(
return rename node $child as ''
)
};
local:updateTitle()
Thanks in advance
There are multiple problems with your query:
Updating functions must be declared as updating.
You're calling another function than you defined (probably you didn't notice as there still have been syntax errors).
Rename node expects some element (or processing instruction, attribute) as target, the empty string is not allowed.
At least BaseX doesn't allow updating statements when defining code as XQuery 1.0. Maybe exist doesn't care about this, try adding it if you need to know.
You do not want to rename, but replace all <childnode />s with its contents, use replace node.
This code fixes all these problems:
declare updating function local:contentUpdate() {
let $root := collection('/lib/repository/content')
return
for $i in $root//childnode
return
replace node $i with $i/data()
};
local:contentUpdate()
eXist-db's XQuery Update syntax is documented at http://exist-db.org/exist/update_ext.xml. Note that this syntax predates the release of the XQuery Update Facility 1.0, so the syntax is different and remains unique to eXist-db.
The way to do what you want in eXist-db is as follows:
xquery version "1.0";
declare function local:contentUpdate() {
let $root := doc('/db/lib/repository/content/content.xml')/root
return
update value $root with $root/string()
};
local:contentUpdate()
The primary changes, compared to your original code, are:
Inserted the eXist-db syntax for your update
Prepended '/db' to your collection name, as /db is the root of the database in eXist-db; replaced the collection() call with a doc() call, since you stated you were operating on a single file, content.xml
Changed //root to /root, since "root" is the root element, so the // (descendant-or-self) axis is extraneous
Replaced updateTitle() with the actual name of the function, contentUpdate
Removed the extraneous namespace declarations
For more on why I used $root/string(), see http://community.marklogic.com/blog/text-is-a-code-smell.

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