Xquery get the children nodes by passing parent value at run time

2.1k Views Asked by At

I need to pass a value at run time get all the childen elements.

For example this is my XML:

<person>
   <details1>
      <name>jack</name>
      <age>26</age>
   </details1>
   <details2>
      <name>john</name>
      <age>48</age>
   </details2>
</person>

And my query:

let $y as xs:string := "details1"
let $x := fn:doc(cts:uri-match('*person.xml'))
return $x/$y

So here I am expecting the result as

<details1>
<name>jack</name>
<age>26</age>
</details1>

but it returns the same name as that of $y i.e. "details1"
or if I query this way

let $y as xs:string := "details1"
let $x := fn:doc(cts:uri-match('*person.xml'))
return $x//$y

the result would be "details1" for 12 times

I am new to XQuery please help me in solving the issue

3

There are 3 best solutions below

1
dirkk On

It seems like you attempt to use a string $y as a node step. However, in XPath/XQuery a path step is different from a string, so you can't simple add a string as path step. Instead, you could look for all descendent elements with the requirement that they do have the same name, i.e.:

let $y as xs:string := "details1"
let $x := fn:doc(cts:uri-match('*person.xml'))
return $x/*[name(.) = $y]
0
Florent Georges On

As drkk said, you have to look at the name of the element. But using name() is dangerous. It returns the lexical name of the element, which might be ns:elem if the element has a prefix. And the prefix can be different in every document, even for the same namespace. So either you match only by local name:

let $y as xs:string := 'details1'
let $x := fn:doc(cts:uri-match('*person.xml'))
return
   $x/*[local-name(.) eq $y]

Or better, match a QName (note the node-name() usage):

let $y as xs:string := xs:QName('details1')
let $x := fn:doc(cts:uri-match('*person.xml'))
return
   $x/*[node-name(.) eq $y]

and if the element name is within a namespace (note the difference between xs:QName() and fn:QName()):

let $y as xs:string := fn:QName('namespace-uri', 'details1')
let $x := fn:doc(cts:uri-match('*person.xml'))
return
   $x/*[node-name(.) eq $y]
2
ehennum On

One alternative is to use xdmp:value() to evaluate the XPath dynamically:

let $y as xs:string := "details1"
let $x := fn:doc(cts:uri-match('*person.xml'))
return xdmp:value("$x/" || $y)

If $x is a large data set, that approach can have performance benefits by removing the need to get the element name for every document in the set.

For more detail, see:

http://docs.marklogic.com/xdmp:value?q=xdmp:value&v=8.0&api=true

Hoping that helps,