[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