Help with a function to return paths
Friday, 16 October 2020 · 4 messages · last reply 17 Oct 2020
AB
Hi Bridger,
How about..
declare function local:pathbuild($result as xs:string,$this as node()){
concat(
$result, "/" , name($this),
$this/@*! concat("[@" , name(.) , "='" , data(.),
"']")=>string-join('')
)
};innermost($input//*)!fold-left(ancestor-or-self::*,'',local:pathbuild#2)
/Andy
Quoted text · Bridger Dyson-Smith · 88 lines
On Fri, 16 Oct 2020 at 16:29, Bridger Dyson-Smith <bdysonsmith at gmail.com> wrote: > Hi all - > > I hope you don't mind a question about serializing distinct XPaths. I'm > trying to generate some reports for documentation and the available > built-in and library functions[1] aren't quite what I need. > > The output I'm after is: > test/aaa/bbb[@type="foo"] > test/aaa/bbb[@type="foo"][@enc="bar"] > test/aaa/bbb[@type="bzz"][@enc="bar"] > test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="start"] > test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="end"] > > I have a couple of functions that are getting me close, but I can't quite > manage the output strings, and multiple children are causing me trouble > (entirely too much like real life). Any help or suggestions would be > greatly appreciated. I've created a gist[2] with the following example: > > Thanks very much for your time and trouble. > Best, > Bridger > > ```xquery > > xquery version "3.1"; > > declare variable $input := > <test> > <aaa> > <bbb type="foo">bbb content</bbb> > </aaa> > <aaa> > <bbb type="foo" enc="bar">bbb content</bbb> > </aaa> > <aaa> > <bbb type="bzz" enc="bar">bbb content</bbb> > <bbb type="qux" enc="bar" key="yes" point="start">bbb content</bbb> > <bbb type="qux" enc="bar" key="yes" point="end">bbb content</bbb> > </aaa> > </test>; > > declare function local:elem( > $nodes as node()* > ) as xs:string* { > for $node in $nodes > return( > string-join( > if ($node/@*) > then (string-join((name($node) || string-join(for $att in $node/@* return local:atty($att))), "/"), local:elem($node/child::*)) > else if ($node/child::*) > then (for $child in $node/child::* return local:elem($child), local:elem($node/child::*)) > else (name($node) || "/" || local:elem($node/child::*)) > ) > ) > }; > > declare function local:atty( > $att as attribute() > ) as xs:string* { > "[@" || name($att) || "='" || data($att) || "']" > }; > > > local:e2($input > (: > this currently returns > test/aaa/bbb[@type='foo']/ > / > /aaa/bbb[@type='foo'][@enc='bar']/ > / > /aaa/bbb[@type='bzz'][@enc='bar']/ > /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='start']/ > /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='end']/ > / > > :) > > ``` > > [1] fn:path, and the related functions from functx > (functx:distinct-element-paths, functx:path-to-node, and > functx:path-to-node-with-pos). The functx functions are really close and > awesome, but I need to incorporate attributes into my output. > [2] https://gist.github.com/CanOfBees/8cfb435ac06986c9b0b0c215a786f4d7 > >
AB
Andy Bunce16 Oct 2020 19:00 #
If you really don't want the leading /
declare function local:pathbuild($result as xs:string,$this as node()){
concat(
$result, '/'[$result], name($this),
$this/@*! concat("[@" , name(.) , "='" , data(.),
"']")=>string-join('')
)
};Quoted text · Andy Bunce · 106 lines
On Fri, 16 Oct 2020 at 17:48, Andy Bunce <bunce.andy at gmail.com> wrote: > Hi Bridger, > > How about.. > > declare function local:pathbuild($result as xs:string,$this as node()){ > concat( > $result, "/" , name($this), > $this/@*! concat("[@" , name(.) , "='" , data(.), > "']")=>string-join('') > ) > }; > > innermost($input//*)!fold-left(ancestor-or-self::*,'',local:pathbuild#2) > > /Andy > > On Fri, 16 Oct 2020 at 16:29, Bridger Dyson-Smith <bdysonsmith at gmail.com> > wrote: > >> Hi all - >> >> I hope you don't mind a question about serializing distinct XPaths. I'm >> trying to generate some reports for documentation and the available >> built-in and library functions[1] aren't quite what I need. >> >> The output I'm after is: >> test/aaa/bbb[@type="foo"] >> test/aaa/bbb[@type="foo"][@enc="bar"] >> test/aaa/bbb[@type="bzz"][@enc="bar"] >> test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="start"] >> test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="end"] >> >> I have a couple of functions that are getting me close, but I can't quite >> manage the output strings, and multiple children are causing me trouble >> (entirely too much like real life). Any help or suggestions would be >> greatly appreciated. I've created a gist[2] with the following example: >> >> Thanks very much for your time and trouble. >> Best, >> Bridger >> >> ```xquery >> >> xquery version "3.1"; >> >> declare variable $input := >> <test> >> <aaa> >> <bbb type="foo">bbb content</bbb> >> </aaa> >> <aaa> >> <bbb type="foo" enc="bar">bbb content</bbb> >> </aaa> >> <aaa> >> <bbb type="bzz" enc="bar">bbb content</bbb> >> <bbb type="qux" enc="bar" key="yes" point="start">bbb content</bbb> >> <bbb type="qux" enc="bar" key="yes" point="end">bbb content</bbb> >> </aaa> >> </test>; >> >> declare function local:elem( >> $nodes as node()* >> ) as xs:string* { >> for $node in $nodes >> return( >> string-join( >> if ($node/@*) >> then (string-join((name($node) || string-join(for $att in $node/@* return local:atty($att))), "/"), local:elem($node/child::*)) >> else if ($node/child::*) >> then (for $child in $node/child::* return local:elem($child), local:elem($node/child::*)) >> else (name($node) || "/" || local:elem($node/child::*)) >> ) >> ) >> }; >> >> declare function local:atty( >> $att as attribute() >> ) as xs:string* { >> "[@" || name($att) || "='" || data($att) || "']" >> }; >> >> >> local:e2($input >> (: >> this currently returns >> test/aaa/bbb[@type='foo']/ >> / >> /aaa/bbb[@type='foo'][@enc='bar']/ >> / >> /aaa/bbb[@type='bzz'][@enc='bar']/ >> /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='start']/ >> /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='end']/ >> / >> >> :) >> >> ``` >> >> [1] fn:path, and the related functions from functx >> (functx:distinct-element-paths, functx:path-to-node, and >> functx:path-to-node-with-pos). The functx functions are really close and >> awesome, but I need to incorporate attributes into my output. >> [2] https://gist.github.com/CanOfBees/8cfb435ac06986c9b0b0c215a786f4d7 >> >>
BD
Bridger Dyson-Smith16 Oct 2020 19:29 #
Hi all -
I hope you don't mind a question about serializing distinct XPaths. I'm trying to generate some reports for documentation and the available built-in and library functions[1] aren't quite what I need.
The output I'm after is:
test/aaa/bbb[@type="foo"]
test/aaa/bbb[@type="foo"][@enc="bar"]
test/aaa/bbb[@type="bzz"][@enc="bar"]
test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="start"] test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="end"]
I have a couple of functions that are getting me close, but I can't quite manage the output strings, and multiple children are causing me trouble (entirely too much like real life). Any help or suggestions would be greatly appreciated. I've created a gist[2] with the following example:
Thanks very much for your time and trouble.
Best,
Bridger
xquery version "3.1";
declare variable $input :=
<test>
<aaa>
<bbb type="foo">bbb content</bbb>
</aaa>
<aaa>
<bbb type="foo" enc="bar">bbb content</bbb>
</aaa>
<aaa>
<bbb type="bzz" enc="bar">bbb content</bbb>
<bbb type="qux" enc="bar" key="yes" point="start">bbb content</bbb>
<bbb type="qux" enc="bar" key="yes" point="end">bbb content</bbb>
</aaa>
</test>;
declare function local:elem(
$nodes as node()*
) as xs:string* {
for $node in $nodes
return(
string-join(
if ($node/@*)
then (string-join((name($node) || string-join(for $att in
$node/@* return local:atty($att))), "/"), local:elem($node/child::*))
else if ($node/child::*)
then (for $child in $node/child::* return
local:elem($child), local:elem($node/child::*))
else (name($node) || "/" || local:elem($node/child::*))
)
)
};
declare function local:atty(
$att as attribute()
) as xs:string* {
"[@" || name($att) || "='" || data($att) || "']"
};
local:e2($input
(:
this currently returns
test/aaa/bbb[@type='foo']/
/
/aaa/bbb[@type='foo'][@enc='bar']/
/
/aaa/bbb[@type='bzz'][@enc='bar']/
/bbb[@type='qux'][@enc='bar'][@key='yes'][@point='start']/
/bbb[@type='qux'][@enc='bar'][@key='yes'][@point='end']/
/
:)[1]
fn:path, and the related functions from functx
(functx:distinct-element-paths, functx:path-to-node, and
functx:path-to-node-with-pos). The functx functions are really close and awesome, but I need to incorporate attributes into my output. [2] https://gist.github.com/CanOfBees/8cfb435ac06986c9b0b0c215a786f4d7BD
Andy, et al,
Thank you for your examples (and apologies - I naturally managed to copy/paste the wrong function into my initial email ).
Higher order functions are, sadly, a bit outside of my wheelhouse, so I'm unable to fully appreciate their power and utility. But! happily I could work from your examples and get something that I can almost wrap my head around! Thank you so much!
Here's the final working example for me, I think:
declare function local:pb(
$result as xs:string,
$node as node()
) {
concat(
$result, "/"[$result], name($node),
$node/@* ! local:atty(.) => string-join("")
)
};
declare function local:atty(
$att as attribute()
) as xs:string* {
"[@" || name($att) || "='" || data($att) || "']"
};
distinct-values(
$input//* ! fold-left(ancestor-or-self::*, '', local:pb(?, ?))
)Again, I really appreciate the guidance!
Thanks and have a pleasant weekend,
Best,
Bridger
Quoted text · Andy Bunce · 118 lines
On Fri, Oct 16, 2020 at 1:01 PM Andy Bunce <bunce.andy at gmail.com> wrote: > If you really don't want the leading / > > declare function local:pathbuild($result as xs:string,$this as node()){ > concat( > $result, '/'[$result], name($this), > $this/@*! concat("[@" , name(.) , "='" , data(.), > "']")=>string-join('') > ) > }; > > On Fri, 16 Oct 2020 at 17:48, Andy Bunce <bunce.andy at gmail.com> wrote: > >> Hi Bridger, >> >> How about.. >> >> declare function local:pathbuild($result as xs:string,$this as node()){ >> concat( >> $result, "/" , name($this), >> $this/@*! concat("[@" , name(.) , "='" , data(.), >> "']")=>string-join('') >> ) >> }; >> >> innermost($input//*)!fold-left(ancestor-or-self::*,'',local:pathbuild#2) >> >> /Andy >> >> On Fri, 16 Oct 2020 at 16:29, Bridger Dyson-Smith <bdysonsmith at gmail.com> >> wrote: >> >>> Hi all - >>> >>> I hope you don't mind a question about serializing distinct XPaths. I'm >>> trying to generate some reports for documentation and the available >>> built-in and library functions[1] aren't quite what I need. >>> >>> The output I'm after is: >>> test/aaa/bbb[@type="foo"] >>> test/aaa/bbb[@type="foo"][@enc="bar"] >>> test/aaa/bbb[@type="bzz"][@enc="bar"] >>> test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="start"] >>> test/aaa/bbb[@type="qux"][@enc="bar"][@key="yes"][@point="end"] >>> >>> I have a couple of functions that are getting me close, but I can't >>> quite manage the output strings, and multiple children are causing me >>> trouble (entirely too much like real life). Any help or suggestions would >>> be greatly appreciated. I've created a gist[2] with the following example: >>> >>> Thanks very much for your time and trouble. >>> Best, >>> Bridger >>> >>> ```xquery >>> >>> xquery version "3.1"; >>> >>> declare variable $input := >>> <test> >>> <aaa> >>> <bbb type="foo">bbb content</bbb> >>> </aaa> >>> <aaa> >>> <bbb type="foo" enc="bar">bbb content</bbb> >>> </aaa> >>> <aaa> >>> <bbb type="bzz" enc="bar">bbb content</bbb> >>> <bbb type="qux" enc="bar" key="yes" point="start">bbb content</bbb> >>> <bbb type="qux" enc="bar" key="yes" point="end">bbb content</bbb> >>> </aaa> >>> </test>; >>> >>> declare function local:elem( >>> $nodes as node()* >>> ) as xs:string* { >>> for $node in $nodes >>> return( >>> string-join( >>> if ($node/@*) >>> then (string-join((name($node) || string-join(for $att in $node/@* return local:atty($att))), "/"), local:elem($node/child::*)) >>> else if ($node/child::*) >>> then (for $child in $node/child::* return local:elem($child), local:elem($node/child::*)) >>> else (name($node) || "/" || local:elem($node/child::*)) >>> ) >>> ) >>> }; >>> >>> declare function local:atty( >>> $att as attribute() >>> ) as xs:string* { >>> "[@" || name($att) || "='" || data($att) || "']" >>> }; >>> >>> >>> local:e2($input >>> (: >>> this currently returns >>> test/aaa/bbb[@type='foo']/ >>> / >>> /aaa/bbb[@type='foo'][@enc='bar']/ >>> / >>> /aaa/bbb[@type='bzz'][@enc='bar']/ >>> /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='start']/ >>> /bbb[@type='qux'][@enc='bar'][@key='yes'][@point='end']/ >>> / >>> >>> :) >>> >>> ``` >>> >>> [1] fn:path, and the related functions from functx >>> (functx:distinct-element-paths, functx:path-to-node, and >>> functx:path-to-node-with-pos). The functx functions are really close and >>> awesome, but I need to incorporate attributes into my output. >>> [2] https://gist.github.com/CanOfBees/8cfb435ac06986c9b0b0c215a786f4d7 >>> >>>
Written for BaseX 9.4. BaseX may have changed since: please check the current documentation.
⚡Generated with XQuery