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()新しい構造を構築する実際の作業を行います。基本的に、入力シーケンスを 3 つの部分にチャンクします。(1) 最初の項目、(2) ネストの優先度が高い連続する後続のすべての項目、および (3) 最初の同等またはそれ以下の優先度の項目とそれに続くすべての項目です。(1) のコピーを作成し、再帰を使用してそのコピー内に (2) をネストし、(3) を再帰して次の同じレベルの兄弟を作成します。

于 2013-09-17T10:24:17.653 に答える