Xquery遍历树

时间:2012-05-17 20:19:52

标签: xquery traversal

我是xquery的新手。我有以下xml文档:

<?xml version="1.0" encoding="UTF-8"?>
     <lines>
        <line>
            <id>1</id>
            <par>1</par>
        </line>
        <line>
            <id>2</id>
            <par>1</par>
        </line>
        <line>
            <id>3</id>
            <par>2</par>
        </line>
        <line>
            <id>4</id>
            <par>2</par>
        </line>
        <line>
            <id>5</id>
            <par>1</par>
        </line>
        <line>
            <id>6</id>
            <par>5</par>
        </line>
        <line>
            <id>7</id>
            <par>5</par>
        </line>
        <line>
            <id>8</id>
            <par>5</par>
        </line>
    </lines>

我想创建一个函数,它将输入一个特定的id,并返回所有的行元素 - 这个id的后代,包括作为输入给出的那个。例如,给出输入1它将返回带有id 1,2,3,4,5,6,7,8的线元素。我知道我可以使用以下内容进入深度1:lines/line[par=id_given],但是如果我想获取所有后代呢?

1 个答案:

答案 0 :(得分:0)

您必须定义一个以递归方式遍历树的函数。这个确实在水平上有效:

declare function local:traverse($tree as element(lines), $id as xs:integer*) as element(line)* {
 let $level := $tree//line[par=$id][id!=$id]
 return
  if ($level)
  then ($level, local:traverse($tree, $level/id))
  else ()
};

您可以使用local:traverse(/lines, 0)调用它。

但是这个函数不会处理你的示例XML,因为这不是树:根元素中有一个循环是不允许的。您将不得不引入一些新的唯一根id或从根中删除父级以获得真正的树。

<lines>
    <line>
        <id>1</id>
        <par>0</par>
    </line>
    [snip]

您还可以更改上面的代码以便能够处理这些自引用:将谓词[id!=$id]添加到$level定义的末尾,这将排除自引用。现在,您的根节点将从结果中排除,将其重新包含在函数调用中:(/lines/line[id=1], local:traverse(/lines, 1))