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

2k 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
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
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
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,