I'm a beginner to XQuery, trying to do some simple exercises to learn it. But this latest query I'm trying to put together refuses to run, giving me a syntax error.
This is my XQuery:
<HTML>
<HEAD><TITLE>Alphabetical Cities Lists</TITLE></HEAD>
<BODY>
{
for $indcode in (65 to 70)
let $indlet := codepoints-to-string($indcode)
return
<H1> {$indlet} LIST </H1>
{
for $cit in doc("mydoc.xml")/CITIES/ENTITY
where starts-with($cit/NAME,$indlet)
order by $cit/NAME
return <LI>{$cit/NAME}</LI>
}
</OL>
}
</BODY>
</HTML>
And this is a subset of my XML file mydoc.xml:
<CITIES>
<ENTITY>
<NAME>Hastings</NAME>
<CITYCOD>230</CITYCOD>
<CNTYCOD>01</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Tilden</NAME>
<CITYCOD>487</CITYCOD>
<CNTYCOD>02</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Alliance</NAME>
<CITYCOD>008</CITYCOD>
<CNTYCOD>07</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Hemingford</NAME>
<CITYCOD>236</CITYCOD>
<CNTYCOD>07</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Ainsworth</NAME>
<CITYCOD>003</CITYCOD>
<CNTYCOD>09</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Kearney</NAME>
<CITYCOD>269</CITYCOD>
<CNTYCOD>10</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Oakland</NAME>
<CITYCOD>358</CITYCOD>
<CNTYCOD>11</CNTYCOD>
</ENTITY>
<ENTITY>
<NAME>Eagle</NAME>
<CITYCOD>159</CITYCOD>
<CNTYCOD>13</CNTYCOD>
</ENTITY>
</CITIES>
What I want it to do is print out a simple HTML document with several lists of these city names, broken out by the letter they start with (A to F).
But when I try running this out on this site, it gives me this error:
Query: <>, line 9, column 6: [XPST0003] syntax error, unexpected "'{'", expecting "'}'"
I have no idea why it says that. I've checked and checked, but my curly braces all seem properly matched. Can anyone see what the problem is? Thanks.
This is a common mistake.
You need to place a , between the <H1> and <OL> nodes.
A single node is an XQuery expression (as are multiple nodes inside a direct element constructor). The return expression must be an expression. In this case you want to return two nodes. Each node is a single item and so in order to return them both you need to concatenate the two nodes in a sequence.
The query would look like this:
<HTML>
<HEAD><TITLE>Alphabetical Cities Lists</TITLE></HEAD>
<BODY>
{
for $indcode in (65 to 70)
let $indlet := codepoints-to-string($indcode)
return
(<H1> {$indlet} LIST </H1>,
<OL>
{
for $cit in doc("mydoc.xml")/CITIES/ENTITY
where starts-with($cit/NAME,$indlet)
order by $cit/NAME
return <LI>{$cit/NAME}</LI>
}
</OL>)
}
</BODY>
</HTML>
You forgot a <OL> before the { on line 9.
So it should be:
<HTML>
<HEAD><TITLE>Alphabetical Cities Lists</TITLE></HEAD>
<BODY>
{
for $indcode in (65 to 70)
let $indlet := codepoints-to-string($indcode)
return
<H1> {$indlet} LIST </H1>
<OL>
{
for $cit in doc("mydoc.xml")/CITIES/ENTITY
where starts-with($cit/NAME,$indlet)
order by $cit/NAME
return <LI>{$cit/NAME}</LI>
}
</OL>
}
</BODY>
</HTML>
Related
I am trying to use a parameterized segment in a th:each, but I am running into this exception:
EL1007E: Property or field 'author' cannot be found on null
If I got it correct, it means that the object whose variables I'm trying to access is null, though through breakpoint and debug in my Spring MVC app I know for sure there are two elements in the list.
Here is the Controller:
#GetMapping("/")
public String getHomePage(Model model) {
log.info("Recupero la home-page");
model.addAttribute("reviews", mainService.getAllReviews());
return "home";
}
Here is the th:each:
<div
th:each="review: ${reviews}"
th:assert="({review} != null)"
th:replace="fragments/utilities :: test(author=${review.author},message=${review.review})"
></div>
Here is the fragment:
<div th:fragment="test(author, message)">
<p th:text="${message}" class="mt-2 text-dark"></p>
<h6 th:text="${author}"></h6>
</div>
Here is a screenshot of the Model at runtime right before returning the webpage to the client:
What's going wrong? Why does it say review object is null?
There is an issue with your main Thymeleaf template. You need to account on Thymeleaf Attribute Precedence which indicates that th:replacewill be running first, before th:each and replace the entire <div> element. The working code may look similar to ...
<th:block th:each="review: ${reviews}">
<div th:replace="fragments/utilities :: test(author=${review.author},message=${review.review})"></div>
</th:block>
I cannot seem to get this right. I have a system where the web user is running a jQuery request to add a new document. The code works perfect if the collection they are storing to have no spaces in the name. This code is:
xquery version "3.0";
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $specialty := request:get-parameter("specialty", "")
let $collection := xmldb:encode-uri($specialty)
let $docnum := request:get-parameter("docnum", "")
let $title := request:get-parameter("title", "")
let $file := concat($docnum, '.xml')
let $path := concat($collection, '/', $file)
let $content := <article>
<div
class="content"
doc="{$docnum}"
title="{xmldb:decode($title)}">
<div
class="panel">
<h1>First Panel</h1>
<p>Content goes here</p>
</div>
</div>
</article>
return
let $new_article := xmldb:store($collection, $file, $content, 'application/xml')
return
let $changepermiss := sm:chmod($new_article, 'rw-rw----')
return
<li
class="file"
data-xpath="{$path}"
>
<a
data-xpath="{$path}"
onclick="doc_open('{$path}')"
href="#"><span
class="glyphicon glyphicon-tags"></span> {$docnum, ': ', xmldb:decode($title)}</a>
</li>
Now, if the collection name that comes from the request contains a space ... like this (after encoding) ... it fails.
"/db/Test/data/Core/Test%20Collection"
The error is:
exerr:ERROR Could not locate collection: /db/Test/data/Core/Test%20Collection
This occurs at this line: xmldb:store($collection, $file, $content, 'application/xml') with $collection not be recognized because of the space.
Now, to test things out, I run this and it is fine.
xquery version "3.0";
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $test := "/db/EIDO/data/Core/Disease%20Prevention"
let $content := <article>
<div
class="content"
doc="D777"
title="Test Document">
<div
class="panel">
<h1>First Panel</h1>
<p>Content goes here</p>
</div>
</div>
</article>
return
let $newdoc := xmldb:store($test, "test.xml", $content, 'application/xml')
return
$newdoc
I cannot understand where I am going wrong in trying to reference the collection with a space in xmldb:store.
NOTE: As I said, the code works if there is no space in the name of the collection.
I have tried many combinations of encoding the URL and making a string from it but nothing seems to work.
If your collection or resource names are possibly going to contain characters that need to be encoded, you need to use xmldb:encode-uri() on the names. In your first sample, you encode the collection name ($collection) but not the resource name ($file). In your second sample, you pre-encode the collection name, but the resource name contains no characters that need to be encoded; so you've fully accounted for the encoding.
Take care to ensure that the collection name, as encoded, does exist. If not, you should pre-create the collection. This could actually be the cause of the error you cite.
When you create links to these resources in HTML output, take care not to double-encode them.
You might find it useful to reference an app that handles encoding well. See the source to eXide https://github.com/wolfgangmm/eXide.
The answer was permission issue (although I am a bit lost at that).
The collection had rw-rw-r-- and the user is in the group and is in fact the owner of the collection.
When I change to include execute (rwxrwxr--) it works as expected. Confusing, but apparently execute permission in required on the collection that is going to be stored into.
I want to return a result on all the books which contain the "databases" attribute in inside of "field".
for $e in //testbook[field="databases"]
return $e/title
Sample of the .xml file:
<?xml version="1.0" encoding="UTF-?>
<testbook>
<book>
<author> AuthorGuy </author>
<title> theBook</title>
<field> databases </field>
</book>
</testbook>
There are two issues:
<title/> and <field/> elements are contained in a <book/> element, and are not direct children of <testbook/>, fix the path expressions.
The <field/> tag contains the field wrapped in whitespace, use contains($string, $needle) instead of a simple comparison.
A working example:
let $document := document{<testbook>
<book>
<author> AuthorGuy </author>
<title> theBook</title>
<field> databases </field>
</book>
</testbook>}
for $e in $document//testbook[contains(book/field, "databases")]
return $e/book/title
I have the following json object -
{
"type": "typeOne",
"Children": [
{
"ChildType": "ChildTypeOne",
"Settings": {
"IsChildTypeOne": true
}
},
{
"ChildType": "ChildTypeTwo",
"Settings": {
"IsChildTypeTwo": true
}
}
]
}
My handlebars template contains the following snippet -
{{#each Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!
{{else}}
ChildTypeTwo!!
{{/if}}
{{/each}}
If I run this data through the template, the only thing that ever renders is ChildTypeTwo!!. So it seems that the if statement isn't properly evaluating IsChildTypeOne. The strange part is that if I put a statement in to display the value of IsChildTypeOne in the else clause, the value is displayed as true for the first ChildType.
Does anyone have any thoughts as to why this is not working as expected?
NOTE - the json posted above is a trimmed down version of my actual object. The real object has nested Children arrays that reuse the same object structure. So for instance, in my example, ChildTypeOne can also have a Childrens array with other objects within it.
EDIT****
So in stepping through the code, I found that if I had my type defined as follows -
...
"Settings" : {
"IsChildTypeOne": 'true'
}
...
it appears to work. Removing the single quoted causes the value to be read as undefined when stepping through.
Given charrs answer didn't seem to help, and the fact that your JSON is more complex than what you've posted, maybe your actual template isn't referencing a parent context correctly? For instance, if you wanted to access the type field in #each children block, it would look like this:
{{#each Children}}
{{#if Settings.IsChildTypeOne}}
{{../type}}
{{/if}}
{{/each}}
This ended up being related to the process being used to serialize the json string into an object. Please see the issue here for an explanation.
Can you try changing the handlebar template code as below:
{{#Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!!
{{else}}
ChildTypeTwo!!!
{{/if}}
{{/Children}}
This would iterate your array of Children and would give you result as
ChildTypeOne!!!
ChildTypeTwo!!!
Since your json has two elements one which has ChildTypeOne true and other not.
Sample handelbar:
<div class="entry">
<h1>{{title}}</h1>
<div class="body">
{{body}}
{{#Children}}
{{#if Settings.IsChildTypeOne}}
ChildTypeOne!!!
{{else}}
ChildTypeTwo!!!
{{/if}}
{{/Children}}
</div>
</div>
The html Output for above template :
<div class="entry">
<h1>My New Post</h1>
<div class="body">
This is my first post!
ChildTypeOne!!!
ChildTypeTwo!!!
</div>
</div>
You can see ChildTypeOne!!! for first element is coming.
I'm trying to prepare an XML file to parse it JSON and its context is such as:
<user_manual>
<embed-language_part id="SL14686180">
<language_part id="1" role="-" lang="de">
<embed-user_manual_part id="1">
<user_manual_part id="1" role="-" document-type="IU">
<embed-chapter id="1">
<?ecls-start-embedded-resource resource="ecls_bio_becls_a3_a30660983"?>
<chapter id="1" role="-" toctitle="yes" footrowtitle="no" type="security">
<embed-title_module id="1">
<title_module id="1" role="-">
<title id="1">Sicherheits- und Warnhinweise</title>
</title_module>
</embed-title_module>
<embed-section id="1">
<section id="1" footrowtitle="no" role="-" toctitle="yes">
<embed-section id="2">
<section id="2">
<embed-title_module id="2">
<title_module id="2" role="-">
<title id="2">Eisschale</title>
</title_module>
</embed-title_module>
</section>
</embed-section>
<embed-title_module id="3">
<title_module id="31" role="-">
<title id="3">Bevor Sie das Gerat in Betrieb nehmen</title>
</title_module>
</embed-title_module>
</section>
</embed-section>
</chapter>
</embed-chapter>
</user_manual_part>
</embed-user_manual_part>
</language_part>
</embed-language_part>
</user_manual>
I wrote an XQuery script regarding to my expectations first (assume that $doc is document, $matnr is 22333),
declare variable $doc external;
declare variable $matnr external;
<dmContainer>{
for $language in $doc/user_manual/embed-language_part/language_part
let $lang_code := data($language/#lang)
for $embed_chapter in $language/embed-user_manual_part/user_manual_part/embed-chapter
let $objectid := data($embed_chapter/processing-instruction('ecls-start-embedded-resource'))[1]
let $fileattr := string($objectid)
let $filename := translate(substring-after($objectid,'resource='),'"','')
let $postfix := substring(tokenize($filename,'_')[last()], 2)
let $name := concat($matnr, '_', $postfix)
return (element {$lang_code} {
attribute title {data($embed_chapter/chapter/embed-title_module/title_module/title)},
attribute language {$lang_code},
attribute name {$name},
for $section in $embed_chapter/chapter/embed-section/section
return <section title="{data($section/embed-title_module/title_module/title)}"></section>
})
}</dmContainer>
This returns:
<dmContainer>
<de title="Sicherheits- und Warnhinweise" language="de" name="223333_30660983">
<section title="Bevor Sie das Gerat in Betrieb nehmen" />
</de>
</dmContainer>
Return contains the chapter element and its first section's title for the JSON but I have to add this one to all sections (the sections included by sections too).
According to the input XML the sections can have another sections (one or more) recursively. You can look the example by searching it deeply. The question is that how i can add these sections to my output with a proper recursive way(i mean not just the child level one level two children are included too) , i searched for some examples recursive functions of XQuery but i couldn't get any one.
Expected output:
<dmContainer>
<de title="Sicherheits- und Warnhinweise" language="de" name="223333_30660983">
<section title="Bevor Sie das Gerat in Betrieb nehmen">
<section title="Eisschale"/>
</section>
</de>
</dmContainer>
How can I get all sections?
If you just want all sections in that chapter (in document order), go with the descendant-or-self-step, abbreviated //.
for $section in $embed_chapter/chapter//embed-section/section
return <section title="{data($section/embed-title_module/title_module/title)}"
If document order doesn't work out for you (eg., first the title of the current section, then all subsections on the current level, no matter if they actually precede the title), you will have to write your own function traversing the tree: a function that gets the current section, returns the title, and recursively calls itself for the direct subsections (if there are any).