Xquery-扁平XML到嵌套XML

时间:2013-09-17 00:48:52

标签: xml xquery

我有一个看起来像这样的平面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”节点下,依此类推。有什么建议吗?

2 个答案:

答案 0 :(得分: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]) }

答案 1 :(得分: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)上递归以创建下一个同级别的兄弟。