我在Xquery中对递归函数进行了练习。我需要仅使用Xquery将平面XML树转换为嵌套XML树。
扁平XML看起来像这样:
<?xml version="1.0"?>
<tree>
<node id="1" type="Folder">
<child>2</child>
<child>3</child>
</node>
<node id="2" type="Folder">
<child>4</child>
</node>
<node id="3" type="Folder">
<child>5</child>
<child>6</child>
</node>
<node id="4" type="Item" />
<node id="5" type="Folder">
<child>7</child>
</node>
<node id="6" type="Item" />
<node id="7" type="Item" />
</tree>
所需的嵌套XML如下所示:
<?xml version="1.0"?>
<tree>
<node id="1" type="Folder" children="2 3">
<node id="2" type="Folder">
<node id="4" type="Item" />
</node>
<node id="3" type="Folder">
<node id="5" type="Folder" children="7">
<node id="7" type="Item" />
</node>
<node id="6" type="Item" />
</node>
</node>
</tree>
我试过没有递归函数的Xquery,但没有太多运气。特别是调理对我来说很奇怪;新嵌套XML的根元素应该是id =“1”的节点,因为它不作为任何其他元素的子节点存在。但是,如果我尝试将此指定为条件,例如在下面,似乎不可能只选择那个节点。
for $node in /Tree/node[@id != /Tree/node/child]
return
<node id="{data($node/@id)}" type="{data($node/@type)}">
(: Lower level subqueries.... :)
</node>
更新:我已经进一步发展了,但是现在我在选择id等于父节点内容的节点时遇到[条件],即递归函数中的for语句没有回馈任何节点,这是意料之外的。
到目前为止,这是我的代码:
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace local = "localhost";
declare option output:method "xml";
declare option output:omit-xml-declaration "no";
declare option output:indent "yes";
declare option output:doctype-system "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
declare function local:addSubNode($n)
{
for $subnode in doc("xml/flat-tree.xml")/tree/node[@id = $n]
let $subnid:=data($subnode/@id)
let $subtype:=data($subnode/@type)
return <node id="{$subnid}" type="{$subtype}">
{local:addSubNode($subnid)}
</node>
};
<tree>
{
for $node in doc("xml/flat-tree.xml")/tree/node[not(@id = /tree/node/child)]
let $nid:=data($node/@id)
return <node id="{$nid}" type="{data($node/@type)}">
{for $child in $node
let $cid:=data($child)
return local:addSubNode($cid)
}
</node>
}
</tree>
答案 0 :(得分:0)
只是部分答案 - =
和!=
可以将原子值与序列进行比较,但它可以作为每个术语的逻辑OR。即“a = b”表示“b中的值是否等于a”,“a!= b”表示“b中的值是否与a不相等”,这不是您要查找的值。你想要“b中没有值等于”是“不是(a = b)”。
for $node in $d/node[not(@id = $d/node/child)] return
<node id="{$node/@id}" type="{$node/@type}">
(: etc. :)
</node>
答案 1 :(得分:0)
好的,明白了!最后我将整个$ node或$ child元素提供给递归函数而不仅仅是它的id属性,现在它可以工作了!
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare namespace local = "localhost";
declare option output:method "xml";
declare option output:omit-xml-declaration "no";
declare option output:indent "yes";
declare option output:doctype-system "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";
(:lookupChildren function :)
declare function local:lookupChildren($p)
{
for $child in $p/child
return $child
};
declare function local:addSubNode($n)
{
for $subnode in doc("xml/flat-tree.xml")/tree/node
let $subnid:=data($subnode/@id)
let $subtype:=data($subnode/@type)
where $subnode/@id = $n/child
return
<node id="{$subnid}" type="{$subtype}" children="{local:lookupChildren($subnode)}">
{local:addSubNode($subnode)}
</node>
};
<tree>
{
for $node in doc("xml/flat-tree.xml")/tree/node[not(@id = /tree/node/child)]
let $nid:=data($node/@id)
let $ntype:=data($node/@type)
return <node id="{$nid}" type="{$ntype}" children="{local:lookupChildren($node)}">
{local:addSubNode($node)}
</node>
}
</tree>