Best way to do dynamic XPath evaluation
Sunday, 10 August 2014 · 4 messages
PS
Paul Swennenhuis10 Aug 2014 21:15 #
Suppose I have a variable $xml containing the XML fragment
<places><village>Wismar</village><city>Amsterdam</city><village>Positano
village, Italy</village><city>Prague</city></places>
In a static world I would list all cities with the XPath expression
$xml//city .
And the villages with $xml//village .OK. No issues. Runs fast.
Now suppose I want the user to decide whether to list either cities or villages.
There are several ways to implement this, but I'm looking for the dynamic XPath solution.
Suppose the users choice ("city" or "village") resides in the string $choice
Something like $xml/$choice abviously will not work. It will yield the
string "village" or "city" 8 times (for all element and text nodes)
xquery:eval() does not help either. xquery:eval($xml/$choice) is not
valid, nor is $xml/xquery:eval($choice) or any variant of these, with or
without parentheses or curly braces.One way I have found to do it is like this:
xquery:eval(concat(fn:serialize($xml),$choice))Complete query:
let $xml :=
<places><village>Wismar</village><city>Amsterdam</city><village>Positano
village, Italy</village><city>Prague</city></places>
let $choice := '//village'
return
xquery:eval(concat(fn:serialize($xml),$choice))But obviously, this executes very slowly because the XML has to be serialized.
So is there any other way to do dynamic joining of XML fragments and XPath expressions given as string?
Oh, BTW, I am NOT looking for this solution:
$xml//*[local-name()=$choice]
Thiis may work for this case but not for other XPath expressions.
DK
Dirk Kirsten10 Aug 2014 21:31 #
Hi Paul,
xquery:eval() should be capable of doing what you want. Take a look at the first example at https://docs.basex.org/wiki/XQuery_Module
Using the empty string for the binding binds the element as context item (thus it must be a document). So the following should work for you: xquery:eval($choice, map { '': document {$xml}})Cheers,
Dirk
Quoted text · Paul Swennenhuis · 50 lines
On 10/08/14 21:15, Paul Swennenhuis wrote: > Suppose I have a variable $xml containing the XML fragment > <places><village>Wismar</village><city>Amsterdam</city><village>Positano > village, Italy</village><city>Prague</city></places> > In a static world I would list all cities with the XPath expression > $xml//city . > And the villages with $xml//village . > > OK. No issues. Runs fast. > > Now suppose I want the user to decide whether to list either cities or > villages. > There are several ways to implement this, but I'm looking for the > dynamic XPath solution. > > Suppose the users choice ("city" or "village") resides in the string > $choice > Something like $xml/$choice abviously will not work. It will yield the > string "village" or "city" 8 times (for all element and text nodes) > xquery:eval() does not help either. xquery:eval($xml/$choice) is not > valid, nor is $xml/xquery:eval($choice) or any variant of these, with or > without parentheses or curly braces. > > One way I have found to do it is like this: > > xquery:eval(concat(fn:serialize($xml),$choice)) > > Complete query: > > let $xml := > <places><village>Wismar</village><city>Amsterdam</city><village>Positano > village, Italy</village><city>Prague</city></places> > > let $choice := '//village' > > return > xquery:eval(concat(fn:serialize($xml),$choice)) > > > But obviously, this executes very slowly because the XML has to be > serialized. > > So is there any other way to do dynamic joining of XML fragments and > XPath expressions given as string? > > Oh, BTW, I am NOT looking for this solution: > $xml//*[local-name()=$choice] > Thiis may work for this case but not for other XPath expressions. > >
--
Dirk Kirsten, BaseX GmbH, http://basex.org
|-- Firmensitz: Blarerstrasse 56, 78462 Konstanz
|-- Registergericht Freiburg, HRB: 708285, Geschäftsführer:
| Dr. Christian Grün, Dr. Alexander Holupirek, Michael Seiferle `-- Phone: 0049 7531 28 28 676, Fax: 0049 7531 20 05 22
DK
Dirk Kirsten10 Aug 2014 21:59 #
Hi Paul,
it should be all there in the documentation I linked to. I really don't know more than the documentation already says :) So to quote:
"Variables and context items can be declared via $bindings. The specified keys must be QNames or strings:
- If a key is a QName, it will be directly adopted as variable name.
- It a key is a string, it may be prefixed with a dollar sign. Namespace can be specified using the Clark Notation.
- If the specified string is empty, the value will be bound to the context item."
So the third bullet points applies here.
Cheers,
Dirk
Quoted text · Paul Swennenhuis · 78 lines
On 10/08/14 21:56, Paul Swennenhuis wrote: > Hi Dirk, > > Cool! Exactly what I was looking for, and it executes fast as well. > This is a life-saver for me. > > Could you elaborate a bit on the "Using the empty string for the binding > binds the element as context item (thus it must be a document)." remark? > I'm not sure if I follow that... > And what are the other binding options? > > Thanks a lot. > > Paul >> Hi Paul, >> >> xquery:eval() should be capable of doing what you want. Take a look at >> the first example at https://docs.basex.org/wiki/XQuery_Module >> >> Using the empty string for the binding binds the element as context item >> (thus it must be a document). So the following should work for you: >> >> xquery:eval($choice, map { '': document {$xml}}) >> >> Cheers, >> Dirk >> >> On 10/08/14 21:15, Paul Swennenhuis wrote: >>> Suppose I have a variable $xml containing the XML fragment >>> <places><village>Wismar</village><city>Amsterdam</city><village>Positano >>> village, Italy</village><city>Prague</city></places> >>> In a static world I would list all cities with the XPath expression >>> $xml//city . >>> And the villages with $xml//village . >>> >>> OK. No issues. Runs fast. >>> >>> Now suppose I want the user to decide whether to list either cities or >>> villages. >>> There are several ways to implement this, but I'm looking for the >>> dynamic XPath solution. >>> >>> Suppose the users choice ("city" or "village") resides in the string >>> $choice >>> Something like $xml/$choice abviously will not work. It will yield the >>> string "village" or "city" 8 times (for all element and text nodes) >>> xquery:eval() does not help either. xquery:eval($xml/$choice) is not >>> valid, nor is $xml/xquery:eval($choice) or any variant of these, with or >>> without parentheses or curly braces. >>> >>> One way I have found to do it is like this: >>> >>> xquery:eval(concat(fn:serialize($xml),$choice)) >>> >>> Complete query: >>> >>> let $xml := >>> <places><village>Wismar</village><city>Amsterdam</city><village>Positano >>> village, Italy</village><city>Prague</city></places> >>> >>> let $choice := '//village' >>> >>> return >>> xquery:eval(concat(fn:serialize($xml),$choice)) >>> >>> >>> But obviously, this executes very slowly because the XML has to be >>> serialized. >>> >>> So is there any other way to do dynamic joining of XML fragments and >>> XPath expressions given as string? >>> >>> Oh, BTW, I am NOT looking for this solution: >>> $xml//*[local-name()=$choice] >>> Thiis may work for this case but not for other XPath expressions. >>> >>> >
--
Dirk Kirsten, BaseX GmbH, http://basex.org
|-- Firmensitz: Blarerstrasse 56, 78462 Konstanz
|-- Registergericht Freiburg, HRB: 708285, Geschäftsführer:
| Dr. Christian Grün, Dr. Alexander Holupirek, Michael Seiferle `-- Phone: 0049 7531 28 28 676, Fax: 0049 7531 20 05 22
PS
Hi Dirk,
Cool! Exactly what I was looking for, and it executes fast as well. This is a life-saver for me.
Could you elaborate a bit on the "Using the empty string for the binding binds the element as context item (thus it must be a document)." remark? I'm not sure if I follow that...
And what are the other binding options?
Thanks a lot.
Paul
Quoted text · Dirk Kirsten · 63 lines
> Hi Paul, > > xquery:eval() should be capable of doing what you want. Take a look at > the first example at https://docs.basex.org/wiki/XQuery_Module > > Using the empty string for the binding binds the element as context item > (thus it must be a document). So the following should work for you: > > xquery:eval($choice, map { '': document {$xml}}) > > Cheers, > Dirk > > On 10/08/14 21:15, Paul Swennenhuis wrote: >> Suppose I have a variable $xml containing the XML fragment >> <places><village>Wismar</village><city>Amsterdam</city><village>Positano >> village, Italy</village><city>Prague</city></places> >> In a static world I would list all cities with the XPath expression >> $xml//city . >> And the villages with $xml//village . >> >> OK. No issues. Runs fast. >> >> Now suppose I want the user to decide whether to list either cities or >> villages. >> There are several ways to implement this, but I'm looking for the >> dynamic XPath solution. >> >> Suppose the users choice ("city" or "village") resides in the string >> $choice >> Something like $xml/$choice abviously will not work. It will yield the >> string "village" or "city" 8 times (for all element and text nodes) >> xquery:eval() does not help either. xquery:eval($xml/$choice) is not >> valid, nor is $xml/xquery:eval($choice) or any variant of these, with or >> without parentheses or curly braces. >> >> One way I have found to do it is like this: >> >> xquery:eval(concat(fn:serialize($xml),$choice)) >> >> Complete query: >> >> let $xml := >> <places><village>Wismar</village><city>Amsterdam</city><village>Positano >> village, Italy</village><city>Prague</city></places> >> >> let $choice := '//village' >> >> return >> xquery:eval(concat(fn:serialize($xml),$choice)) >> >> >> But obviously, this executes very slowly because the XML has to be >> serialized. >> >> So is there any other way to do dynamic joining of XML fragments and >> XPath expressions given as string? >> >> Oh, BTW, I am NOT looking for this solution: >> $xml//*[local-name()=$choice] >> Thiis may work for this case but not for other XPath expressions. >> >>
Written for BaseX 7.9. BaseX may have changed since: please check the current documentation.
⚡Generated with XQuery