0

我有一个看起来像这样的平面 XML 文件:

<Data>
   <DataType1>1</DataType1>
   <DataType2>2</DataType2>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
   <DataType1>1</DataType1>
   <DataType2>2</DataType2>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
</Data>

我希望能够使用 Xquery 嵌套它,看起来像这样:

 <Data>
       <DataType1>1
          <DataType2>2
             <DataType3>3
                <DataType4>4</DataType4>
             </DataType3>
          </DataType2>
             <DataType3>3
                <DataType4>4</DataType4>
            </DataType3>
      </DataType1>
       <DataType1>1
          <DataType2>2
             <DataType3>3
                <DataType4>4</DataType4>
             </DataType3>          
         </DataType2>
      </DataType1>
 </Data>

我基本上是想按顺序嵌套,所以任何“2”都会嵌套在前面的“1”节点下,以此类推。有什么建议么?

4

2 回答 2

0

编写一个用于取消嵌套数据的递归函数。我添加了一个“调试属性”用于测试目的,请随意删除它(以及带有匹配注释的行)。

declare function local:unflatten($xs as node()*) {
  for $x in $xs
  return
    let $next := ($x/following-sibling::*[number(text()) <= number($x/text())])[1]
    return
      element { concat("DataType", $x/text()) } {
        attribute id { $x/@id }, (: remove debug output here :)
        $x/text(),
        "&#10;", (: Print newline character :)
        local:unflatten($x/following-sibling::*[
              (empty($next) or . << $next)
            and
              number(text()) = $x/text() + 1
          ]
        )
      }
};

let $xml := <Data>
   <DataType1 id="1">1</DataType1>
   <DataType2 id="2">2</DataType2>
   <DataType3 id="3">3</DataType3>
   <DataType4 id="4">4</DataType4>
   <DataType3 id="5">3</DataType3>
   <DataType4 id="6">4</DataType4>
   <DataType1 id="7">1</DataType1>
   <DataType2 id="8">2</DataType2>
   <DataType3 id="9">3</DataType3>
   <DataType4 id="10">4</DataType4>
</Data>

return element Data { local:unflatten($xml/*[text()=1]) }
于 2013-09-17T08:58:03.190 回答
0

我假设您的示例输出中第一个 DataType2 元素的结束标记的位置是一个错字,并且它实际上应该出现以下 DataType3 之后。

以下在 eXist-db 中对我有用

xquery version "1.0";

declare function local:getLevel($n) as xs:integer {
    xs:integer(substring-after($n/local-name(), 'DataType'))
};

declare function local:buildHierarchy($seq) {
    if (empty($seq)) then ()
    else
    let $lvl := local:getLevel($seq[1]),
        $until := (
            for $el at $p in $seq
            where $p gt 1 and local:getLevel($el) le $lvl
            return $p
            ,
            count($seq) + 1
        )[1]
    return (
        element { node-name($seq[1]) } {
            $seq[1]/text(),
            local:buildHierarchy(subsequence($seq, 2, $until - 2))
        },
        local:buildHierarchy(subsequence($seq, $until))
    )
};

let $data := <Data>
   <DataType1>1</DataType1>
   <DataType2>2</DataType2>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
   <DataType1>1</DataType1>
   <DataType2>2</DataType2>
   <DataType3>3</DataType3>
   <DataType4>4</DataType4>
</Data>
return
    <Data>{local:buildHierarchy($data/*)}</Data>

local:getLevel()确定元素的嵌套优先级(在这种情况下,只需返回元素名称中的数字)。嵌套优先级高于其前一个兄弟元素的元素将嵌套在其中。

local:buildHierarchy()做构建新结构的真正工作。它基本上将其输入序列分为三部分:(1)第一个项目,(2)所有具有更高嵌套优先级的连续后续项目,以及(3)第一个相等或较低优先级的项目以及所有后续项目。它制作 (1) 的副本并使用递归将 (2) 嵌套在该副本中,然后在 (3) 上递归以创建下一个相同级别的兄弟姐妹。

于 2013-09-17T10:24:17.653 回答