我有一个元素列表,其中包含有关它们在XML树中的深度的信息。 “底部”的元素,即在具有较低深度的元素之前出现的元素,包含文本。
<input>
<text n="x" xml:id="a" depth="1"/>
<div xml:id="b" depth="2"/>
<div xml:id="c" depth="3"/>
<p xml:id="e" depth="4">text</p>
<p xml:id="d" depth="4">text</p>
<p xml:id="x" depth="4">text</p>
<div xml:id="f" depth="3"/>
<lg xml:id="j" depth="4"/>
<l xml:id="k" depth="5">text</l>
<l xml:id="l" depth="5">text</l>
<p xml:id="n" depth="3">text</p>
</input>
我想在一个操作中将其重构为下面的XML树。
<text n="x" xml:id="a" depth="1">
<div xml:id="b" depth="2">
<div xml:id="c" depth="3">
<p xml:id="e" depth="4">text</p>
<p xml:id="d" depth="4">text</p>
<p xml:id="x" depth="4">text</p>
</div>
<div xml:id="f" depth="3">
<lg xml:id="j" depth="4">
<l xml:id="k" depth="5">text</l>
<l xml:id="l" depth="5">text</l>
</lg>
</div>
<p xml:id="n" depth="3">text</p>
</div>
</text>
我想我需要从最高深度的元素开始,即深度为5的所有元素,然后将它们包裹在深度为5-1的前一个元素中,依此类推,但我无法获得我的头脑如何通过这个来解决。
@xml:id仅供参考。
我的问题与我之前的stackoverflow问题相反。它类似于stackoverflow这个问题,但我需要使用XQuery。
答案 0 :(得分:3)
构建一个递归构建树的函数。这段代码非常通用,通过更改它应该适用于任意“扁平”树的local:getLevel($node)
函数。
declare function local:getLevel($node as element()) as xs:integer {
$node/@depth
};
declare function local:buildTree($nodes as element()*) as element()* {
let $level := local:getLevel($nodes[1])
(: Process all nodes of current level :)
for $node in $nodes
where $level eq local:getLevel($node)
(: Find next node of current level, if available :)
let $next := ($node/following-sibling::*[local:getLevel(.) le $level])[1]
(: All nodes between the current node and the next node on same level are children :)
let $children := $node/following-sibling::*[$node << . and (not($next) or . << $next)]
return
element { name($node) } {
(: Copy node attributes :)
$node/@*,
(: Copy all other subnodes, including text, pi, elements, comments :)
$node/node(),
(: If there are children, recursively build the subtree :)
if ($children)
then local:buildTree($children)
else ()
}
};
let $xml := document {
<input>
<text n="x" xml:id="a" depth="1"/>
<div xml:id="b" depth="2"/>
<div xml:id="c" depth="3"/>
<p xml:id="e" depth="4">text</p>
<p xml:id="d" depth="4">text</p>
<p xml:id="x" depth="4">text</p>
<div xml:id="f" depth="3"/>
<lg xml:id="j" depth="4"/>
<l xml:id="k" depth="5">text</l>
<l xml:id="l" depth="5">text</l>
<p xml:id="n" depth="3">text</p>
</input>
}
return local:buildTree($xml/input/*)
特此我将此代码发布到公共领域。
如果你的XQuery处理器不支持增强的FLWOR表达式,你需要重新排序一些行;我省略了评论:
for $node in $nodes
let $level := local:getLevel($nodes[1])
let $next := ($node/following-sibling::*[local:getLevel(.) le $level])[1]
let $children := $node/following-sibling::*[$node << . and (not($next) or . << $next)]
where $level eq local:getLevel($node)
答案 1 :(得分:2)
只是提出另一种方法 - 我不认为我之前曾在愤怒中使用过交叉!
declare function local:buildTree($nodes,$level) {
for $node in $nodes[@depth=$level]
let $end := $node/following-sibling::*[@depth = $level][1]
let $rest :=
if ($end)
then $node/following-sibling::* intersect $end/preceding-sibling::*
else $node/following-sibling::*
return
element {$node/name()} {
$node/@*,
$node/node(),
local:buildTree($rest,$level+1)
}
};
declare function local:buildTree($node) {
local:buildTree($node/*,1)
};
let $xml := document {
<input>
<text n="x" xml:id="a" depth="1"/>
<div xml:id="b" depth="2"/>
<div xml:id="c" depth="3"/>
<p xml:id="e" depth="4">text</p>
<p xml:id="d" depth="4">text</p>
<p xml:id="x" depth="4">text</p>
<div xml:id="f" depth="3"/>
<lg xml:id="j" depth="4"/>
<l xml:id="k" depth="5">text</l>
<l xml:id="l" depth="5">text</l>
<p xml:id="n" depth="3">text</p>
</input>
}
return local:buildTree($xml/input)
答案 2 :(得分:1)
这是另一个版本,基于Chris Wallace的方法,但使用XQuery 3.0的tumbling window
构造,这使得此代码稍微简单。
declare function local:buildTree($nodes,$level) {
for tumbling window $node-window in $nodes
start $start when $start/@depth = $level
let $rest := fn:tail($node-window)
return
element {$start/fn:name()} {
$start/@*,
$start/node(),
local:buildTree($rest,$level+1)
}
};
declare function local:buildTree($node) {
local:buildTree($node/*,1)
};
let $xml := document {
<input>
<text n="x" xml:id="a" depth="1"/>
<div xml:id="b" depth="2"/>
<div xml:id="c" depth="3"/>
<p xml:id="e" depth="4">text</p>
<p xml:id="d" depth="4">text</p>
<p xml:id="x" depth="4">text</p>
<div xml:id="f" depth="3"/>
<lg xml:id="j" depth="4"/>
<l xml:id="k" depth="5">text</l>
<l xml:id="l" depth="5">text</l>
<p xml:id="n" depth="3">text</p>
</input>
}
return local:buildTree($xml/input)