[xquery-talk] creating tree from sequence (from tree)
G. Ken Holman
gkholman at CraneSoftwrights.com
Sat May 7 06:03:48 PDT 2011
I took it as a challenge to use an arbitrary node set as you posited.
At 2011-05-06 16:29 -0700, trubliphone wrote:
>Now, suppose there is a user-provided XPath expression to find
>particular nodes in that file:
>
>$query := "//foo/bar"
>
>I can evaluate that expression as follows:
>
>$doc := doc("myDocument.xml")
>$nodes := util:eval(concat("$doc",$query)
Lucky you ... I'm unfamiliar with util:eval() and so in my solution I
just use standard XQuery. I'll just use the following in place of
your code above:
let $nodes:= doc('t.xml')//foo/bar
>This will return a sequence:
>
>(<bar>one</bar>, <bar>two</bar>, <bar>three</bar>, <bar>four</bar>,
><bar>five</bar>)
>
>But I need to recreate the original hierarchical structure of those
>nodes. So what I really want is this:
>
><bar>one
> <bar>two</bar>
> <bar>three</bar>
></bar>
><bar>four
> <bar>five</bar>
></bar>
Which again means, I feel, it is easier to walk the original tree and
only produce nodes when you need them. But with your constraint of
building a sequence of candidate nodes to work with, that changes the
way nodes in the tree are identified.
So I used node identity to determine while walking the tree if the
node I'm at is one of the nodes of interest.
I hope the solution below helps.
. . . . . . . Ken
T:\ftemp>type t.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo>
<bar>one</bar>
<foo>
<bar>two</bar>
</foo>
<foo>
<bar>three</bar>
</foo>
</foo>
<foo>
<bar>four</bar>
<foo>
<foo>
<bar>five</bar>
</foo>
</foo>
</foo>
</root>
T:\ftemp>xquery t.xq
<?xml version="1.0" encoding="UTF-8"?>
<bar>one<bar>two</bar>
<bar>three</bar>
</bar>
<bar>four<bar>five</bar>
</bar>
T:\ftemp>type t.xq
declare function local:generate-id( $node as node() ) as xs:string
{
(:replace this in the future with a standard implementation of generate-id:)
concat('N',count($node/preceding::node()))
};
declare function local:outDescendants( $nodes as node()* ) as node()*
{
let $nodeIds := $nodes/local:generate-id(.)
for $node in $nodes return
let $ancestors :=
$node/ancestor::*/(* except $node)[local:generate-id(.) = $nodeIds]
let $descendants :=
$node/..//(* except $node)[local:generate-id(.) = $nodeIds]
return if ( $ancestors )
then () (:this node has already been handled:)
else if ( $descendants )
then (:this node's descendants are in the list:)
element { local-name($node) }
{
( $node/text(),
local:outDescendants( $descendants )
)
}
else (:this is a leaf node and belongs in the result:)
$node
};
let $nodes:= doc('t.xml')//foo/bar
return local:outDescendants( $nodes )
T:\ftemp>
--
Contact us for world-wide XML consulting & instructor-led training
Crane Softwrights Ltd. http://www.CraneSoftwrights.com/q/
G. Ken Holman mailto:gkholman at CraneSoftwrights.com
Legal business disclaimers: http://www.CraneSoftwrights.com/legal
More information about the talk
mailing list