• ベストアンサー

多次元配列からxmlツリーを作成したい

はじめて投稿させていただきます。 DBに登録されているツリーテーブルを配列に取り込み、 xmlのツリーにしてファイルに保存したいと考えております。 DBのデータは以下のようになっております。 id cid att name 0 -1 D a 1 0 F b 2 0 F c 中略 33 -1 D d 34 33 D e 35 34 F f 36 34 F g 中略 262 -1 D h 263 262 D i 264 263 F j id:インデックス cid:子番号(idに同じ番号を持つものを親とする)(-1は最上階層の親) att:DはディレクトリFは末端 name:表示する名前 これを展開しながらxmlタグを挿入していきたいのですが nameを出力するタイミングでタグを挿入しようとすると末端のものしか 開始・終了タグで挟むことができません。 入れ子になっている場合の親の終了タグは全ての子供が終わった後に、階層の深さの数だけを最後に足してやりたいのです。 不規則な配列なのでforeachでするくらいしかわからず、 やってみたのですがatt=="D"なら再帰処理で繰り返し、 Dでなくなったら末端を書くくらいの事しかできず、 終了タグをどういった処理で入れたらよいかわかりません。 800文字制限に引っかかってしまい実際の配列の形はのせれませんでした。。 分割投稿も禁止のようで。。 各要素を持つ末端配列はわかりやすいように、仮に一つであっても次元を一つ下げて0番目の配列に格納しております。 もしかしたら配列への格納方法もよくないのかもしれません。。 ・後々扱いやすいような配列への格納の仕方 ・配列を展開しながらのタグの挿入方法 をどなたか良い案がありましたら宜しくお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
回答No.3

面白そうなので、プログラムを書いてしまいました^^。 DBからデータを読みながら処理するのではなく、DBからのデータを一旦$dbdataに入れて処理をする前提のプログラムです(DBからSELECTする部分は省略してます)。DBのデータが、きれいな順番で並んでいないことがあるならば、そうしないとうまく処理できないと思います。 内部的にはDOMでデータを扱ってます。 >・後々扱いやすいような配列への格納の仕方 配列よりもクラスのオブジェクトにするのが一番扱いやすいと思います。クラスを自作しても良いのですが、色々と機能を追加していくとDOMに近づいていくように思います。なので、DOMを使っています。 それから、ディレクトリ階層を下に辿っていく処理は再帰を利用しています。 <?php $dbdata  = array(    array('id'=>'0','cid'=>'-1','attr'=>'D','name'=>'a'),    array('id'=>'1','cid'=>'0','attr'=>'F','name'=>'b'),    array('id'=>'2','cid'=>'0','attr'=>'F','name'=>'c'),    array('id'=>'33','cid'=>'-1','attr'=>'D','name'=>'d'),    array('id'=>'34','cid'=>'33','attr'=>'D','name'=>'e'),    array('id'=>'35','cid'=>'34','attr'=>'F','name'=>'f'),    array('id'=>'36','cid'=>'34','attr'=>'F','name'=>'g'),    array('id'=>'262','cid'=>'-1','attr'=>'D','name'=>'h'),    array('id'=>'263','cid'=>'262','attr'=>'D','name'=>'i'),    array('id'=>'264','cid'=>'263','attr'=>'F','name'=>'j'),   ); // 扱いづらいので、以下のようなハッシュ形式データにする。 // array('0' => array('cid'=>'-1','attr'=>'D','name'=>'a'), //   '1' => array('cid'=>'0','attr'=>'F','name'=>'b'), $hash = array(); foreach ($dbdata as $arr) {  $id = $arr['id'];  unset($arr['id']);  $hash[$id] = $arr; } // DOMDocument作成 $doc = new DOMDocument('1.0', 'UTF-8'); $doc->formatOutput = true; // hoge呼び出し $hoge = new hoge($doc, $hash); $doc->appendChild($hoge->exec()); echo $doc->saveXML(); class hoge {  var $doc;  var $hash;  function hoge($doc, $hash) {   $this->doc = $doc;   $this->hash = $hash;  }  function exec($id = '-1') {   // 初回呼び出し   if ($id == '-1') {    // root要素作成    $retnode = $this->doc->createElement('root');   }   // 初回以外   else {    // attr (D or F)    $attr = $this->hash[$id]['attr'];    // name    $name = $this->hash[$id]['name'];    // D or Fという要素作成    $retnode = $this->doc->createElement($attr);    // id属性設定    $retnode->setAttribute('id', $id);    // name属性設定    $retnode->setAttribute('name', $name);   }   // 初回 or attr == D の場合(子供がいる)   if ($id == '-1' || $attr == 'D') {    foreach ($this->hash as $k => $v) {     // 子要素か?     if ($v['cid'] == $id) {      // 再帰呼び出しして、$retnodeの子要素にする      $retnode->appendChild($this->exec($k));     }    }   }   return $retnode;  } } ?> インデントは全角スペースを使っているので、半角スペースに置き換えてください。 なお、数日間出掛けてしまうので、追加のご質問頂いても返答できないかもしれません。申し訳ありません。 もし何かの役に立てば幸いです。

gogo-tea
質問者

お礼

具体的にプログラムを作っていただきありがとうございます! 私の方も休み明けにならないとソースを触れないので 休み明けになりましたら早速試してみます! もし実際に組んでみて質問したいことがでてきたら数日後なら 大丈夫でしょうか^^; その時はまた宜しくお願い致します。

gogo-tea
質問者

補足

書き込みをするスペースがここくらいしかなかないようなのでここで報告させていただきます。 結論から先に言いますと希望する挙動を得ることができました。 ありがとうございましたm(_ _)m PHPマニュアルページのDOMの部分とにらめっこしながらfdsjaklfjas様が書いてくださったソースを実データに合わせた形に変更し、DBからデータを取得してスクリプトを走らせたところ日本語がやはり化けましたので、取得しながらエンコードをかけたらうまくいきました。 $doc = new DOMDocument('1.0', 'UTF-8'); この段階で $doc = new DOMDocument('1.0', 'Shift-JIS'); とすることは出来ないのですね^^; あと今回は <entity id="1" pic="hoge.jpg" t_name="item_0001" url="contents" image="images/dir.gif" imageopen="images/diropne.gif" name="商品1"/> のようにid等必要なものを全てentity要素の属性として1つのタグの中に書く事をfdsjaklfjas様のソースから教えていただいたのですが、 上記タグを例えば <entity id="1">  <pic>hoge.jpg</pic>  <t_name>item_0001</t_name>  <url>contents</url>  <image>images/dir.gif</image>  <imageopen>images/diropne.gif</imageopen>  <name>商品1</name> </entity> のようにentity配下のpic、t_name要素というような形を生成することは可能なのでしょうか? createElementで作成してappendChildで追加するとどうしても <pic>は<pic />というタグ1つで終了する形になってしまいまして。。 勉強の為、もし宜しければ今回作成していただいたプログラムを題材にそういったことが出来るのなら教えてくださいm(_ _)m

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (3)

回答No.4

>のようにentity配下のpic、t_name要素というような形を生成することは可能なのでしょうか? $e1 = $doc->createElement('entity'); $e2 = $doc->createElement('pic', 'hoge.jpg'); $e3 = $doc->createElement('t_name', 'item_0001'); $e1->appendChild($e2); $e1->appendChild($e3); でどうでしょうか。

gogo-tea
質問者

お礼

回答ありがとうございます。 待っている間に考えていました。  <?xml version="1.0" encoding="utf-8" ?>  - <entity>   - <aaa>    - <bbb>      <pic>hoge.jpg</pic>      <t_name>item_0001</t_name>     </bbb>    </aaa>   </entity> 上のような形ってどうやるんだろうって思っていたのですが <?php $doc = new DOMDocument('1.0', 'utf-8'); $e1 = $doc->createElement('entity'); $e2 = $doc->createElement('aaa'); $e3 = $doc->createElement('bbb'); $e4 = $doc->createElement('pic', 'hoge.jpg'); $e5 = $doc->createElement('t_name', 'item_0001'); $e3->appendChild($e4); $e3->appendChild($e5); $e2->appendChild($e3); $e1->appendChild($e2); $doc->appendChild($e1); echo $doc->saveXML(); ?> のように深い階層から順番にappendChildを記述していけば良いのですね^^ DOMDocumentというのは今回初めて使ってみたのですがすごく勉強になりました。 どうもありがとうございました m(_ _)m

全文を見る
すると、全ての回答が全文表示されます。
noname#39970
noname#39970
回答No.2

多分、DOMを使った方がいいかもしれない。。。 自分も勉強中だからまだよくわからないけど新規に作るなら $doc=new DOMDocument('1.0','UTF-8'); $doc->appendChild($doc->createElement('root')); でもこれver5じゃないと使えないみたい。

gogo-tea
質問者

お礼

DOMDocumentを使用して fdsjaklfjas様のソースをこちらの環境に合わせながら なんとか希望通りのものを作成することができました。 DOMDocumentってすごいですね^^; ありがとうございました。

gogo-tea
質問者

補足

アドバイスありがとうございます >でもこれver5じゃないと使えないみたい。 ver5とはPHP5ということでしょうか? もしそうでしたらこちらの環境はPHP5です。 ですが、DOMDocumentというのを使用すればまっさらな状態から 不規則階層のxmlツリーを作成できるのでしょうか? 現実問題として不規則階層を持つツリーデータをDBから取り出して xmlツリーを作成すること自体は可能なのでしょうかね・・・汗 階層数があらかじめ決まっていれば何とかなりそうな予感はするのですが。。。 引き続き、良い方法がありましたらどなたかご教授お願い致しますm(__)m

全文を見る
すると、全ての回答が全文表示されます。
noname#39970
noname#39970
回答No.1

単純ループじゃなく再帰呼出(リカーシブコール)系で組み直すとか それだと効率悪いか・・・もう少し考えないとだめかな・・・

gogo-tea
質問者

補足

以下のような再帰処理で配列を作ってみたのですが この配列でその後の処理がいけるのかいけないのか・・・。 自分の力ではここからxmlツリーを作成することが出来ませんでした。。 function GetTree($val) { $sql = "select * from tree_tbl where cid = ".$val." order by id"; if(!($result = mysql_query($sql))) { return; } $leaves = ""; while($row = mysql_fetch_assoc($result)) { $leaf = ""; $leaf[0] = $row; //子がある場合 if($row["att"]=="D"){ $leaf[1] = GetTree($row["id"]); } $leaves[] = $leaf; } return $leaves; } $tree = GetTree(-1); print_r($tree);

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 多次元配列を2次元の連想配列で表したい

    PHP初心者です。 $category = array( array( 'ライフ' ), array( 'デジタルライフ', array( '携帯・PHS' ), array( 'ワンセグ放送' ) ), array( '趣味', array( 'AV機器', array( 'オーディオ' ), ), ), ); このような多次元配列を $new_category = array( array( 'id'=> '1', 'parent_id' => '-', 'name' => 'ライフ', ), array( 'id'=> '2', 'parent_id' =>'-', 'name' => 'デジタルライフ', ), array( 'id'=> '3', 'parent_id' => '2', 'name' => '携帯・PHS', ), array( 'id'=> 4, 'parent_id' => '2', 'name' => 'ワンセグ放送', ), array( 'id'=> 5, 'parent_id' => '-', 'name' => '趣味', ), array( 'id'=> '6', 'parent_id' => '5', 'name' => 'AV機器', ), ); このような二次元配列に変換したいです。 parent_idを入れておく配列を作って、階層が深くなったら前の要素のIDを配列に入れ、浅くなったら配列の末尾の要素を消去する…みたいなことを考えてあれこれやってみたのですが、なかなかうまくいきません。 何か良い方法がありましたら、教えていただけるとありたがいです。 よろしくお願い致します。

    • ベストアンサー
    • PHP
  • 多次元配列を、1次元の配列にする関数を書いてください。

    以下のように、多次元配列の配列をペチャンコにする関数(array_flatten)ってどうかきますか? <?php $arr[0] = 'a'; $arr[1][0] = 'b'; $arr[1][1][0] = 'c'; $arr[1][1][1][0] = 'd'; $arr[1][1][1][1][0] = 'e'; $arr[1][1][1][2] = 'f'; $arr[1][1][2] = 'g'; $arr[1][2] = 'h'; $arr[2] = 'i'; function array_flatten($arg){   処理; } $new_arr = array_flatten($arr); print_r($new_arr); ?> ↓ Array (   [0] => a   [1] => b   [2] => c   [3] => d   [4] => e   [5] => f   [6] => g   [7] => h   [8] => i ) ※関数のなかで、ペチャンコの配列を格納するための新しい配列を宣言すると、 同関数を再帰的に呼び出したら、その配列が初期化されますよね?

    • ベストアンサー
    • PHP
  • 配列のソート

    下記のような形でデータを取得し結果を配列に格納し、 降順にソートしたいのですが、いい方法が見つかりません。いい方法はあるでしょうか。よろしくお願いします。 テーブル構造(test) ID|name |point|area| ==================== 1 |Aさん|56 | A | 2 |Bさん|12 | B | 3 |Cさん|24 | B | 4 |Dさん|34 | B | $sql = "select * from test"; $result = mysql_query($strSQL); while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { ここで配列に格納 } 配列への格納方法と、pointの降順にソートする 方法が知りたいです。 最終的に、Aさん、Dさん、Cさん、Bさんと なるようにしたいです。

    • ベストアンサー
    • PHP
  • Smarty 三次元配列を使いたい

    最近Smartyを使い始めた者です。 以下のような多次元配列を、$smarty->assign('data', $data); します。 $data = array( [0] => array( [id] => 23 [comment] => 'aaa' [name] = array( [0] => 'xxx' [1] => 'yyy' )) [1] => array( [id] => 24 [comment] => 'bbb' [name] = array( [0] => 'zzz' )) [2] => array( [id] => 25 [comment] => 'ccc' [name] = array( [0] => 'ttt' [1] => 'uuu' )) ) そして、以下のようなテーブルを表示させたいのです。 |-------------------| | 23  | aaaa | xxxx | |    |     |------| |    |     | yyyy | |-------------------| | 24  | bbbb | zzzz | |-------------------| | 25  | cccc | tttt | |    |     |------| |    |     | uuuu | |-------------------| <td>タグのrowspan属性を使おうと思っています。 {foreach}をつかって、以下のようなものを書きましたが、駄目でした。 {foreach from=$data item=value01 name=kiji} <tr> <td rowspan="{$value01.numgoods}">{$value01.id}</td> <td rowspan="{$value01.numgoods}">{$value01.comment}</td> {foreach from=$value01 item=value02 name=goods} {if $smarty.foreach.goods.first} <td>{$value02.name.0]</td></tr> {else} <tr> <td>{$value.name.1}</td> </tr> {/if} {/foreach} {/foreach} エラー表示は、 syntax error: unrecognized tag: $value02.name.0 です 本当は、$value02.name の.0を三次元での要素数分だけ自動でループしてほしいのですが、その書き方がわかりません。 ネットをあさっても、これと言うサイトを見つけれません。 そもそも、三次元目の要素を取得するsmartyの方法がわかりません。 どなたかご教授の程、よろしくお願いいたします。

    • ベストアンサー
    • PHP
  • phpmyadminからphpに読み込ませて配列化

    現在phpmyadminからphpに読み込みこんで配列化する勉強をしています。 syouhinテーブルの中にid name priceというものを用意しています。 $recordSet = mysql_query('SELECT * FROM syouhin'); while($data = mysql_fetch_assoc($recordSet)) { echo $data['id'],$data['name'],$data['price']; } このようにすれば用意しているもの、例えばidが1、nameがバナナ,priceが200、idが2、nameがバナナ,priceが200・・・・と全部でてくると思います。 これは$dataにこれらが入っているのだと思うですが、idが1のものをsyouhin1に格納しidが2のものをsyouhin2に格納するようにしたいのですがどうしたらいいでしょうか? すみません説明がへたくそでして。 プログラムは授業で勉強したばかりで配列が弱いので、丁寧に教えて頂けると嬉しいです。 ここの勉強もしておくべきという場所もありましたら教えて頂けると嬉しいです。 よろしくお願いします。

    • ベストアンサー
    • PHP
  • 多次元配列のスマートな書き方

    $key文字列の:区切りで階層化した多次元配列に$valを代入するのですが 階層が深くなるにつれてcaseの記述も増えるため、ほとんど無限?に深くても 代入できるように(イ)の部分を変えたいのですが、スマートに記述する方法はありますか? <?php $key = "A:B:C"; ////$key = "A:B:C:D:E:F:G"; // この場合はcase 7まで書かなければならない? $val = "test"; $soe = split(":", $key); // -------------------------- イ switch (count($soe)) { case 1: $atr[$soe[0]] = $val; break; case 2: $atr[$soe[0]][$soe[1]] = $val; break; case 3: $atr[$soe[0]][$soe[1]][$soe[2]] = $val; break; default: print "ERR!"; exit; } // -------------------------- イ print_r($atr); exit; ?>

    • 締切済み
    • PHP
  • 2次元配列のソート

    下記3つの2次元配列を個人コードをキーとして昇順に新しい2次元配列Dへ格納する場合、どのようなやり方が最も速く出来るでしょうか? ご教授願います。 出来ればサンプルプログラムなど書いて頂けると非常にありがたいです。 A vntArray(0,0)=001・・・個人ID vntArray(0,1)=aaa・・・個人コード vntArray(0,2)=田中・・・氏名 vntArray(1,0)=005 vntArray(1,1)=eee vntArray(1,2)=中村 B vntArray(0,0)=003 vntArray(0,1)=ccc vntArray(0,2)=菊地 C vntArray(0,0)=002 vntArray(0,1)=bbb vntArray(0,2)=阿部 vntArray(0,0)=004 vntArray(0,1)=ddd vntArray(0,2)=川上

  • xmlの値を配列変数に格納したい。

    http://xml-jp.amznxslt.com/onca/xml3?&t=saite-22&dev-t=**************&BrowseNodeSearch=562014&mode=dvd-jp&type=lite&locale=jp&page=1&f=xml これのXML文書を /Details/ProductNameの値をとりだし、 name(i)という配列変数にひとつずつ格納たいのですが、どのようにコーディングすればよいでしょうか? 教えてください。

  • 配列のbindValue方法について

    <環境> サーバーOS:CentOS webサーバー:apache データベース:MySQL PHP5.2 現在、上記の環境でwebアプリを作成しています。 データベースにデータを登録する際にPDOを使用しているのですが、 二次元配列に格納されているデータを bindValue() できずに困っています。 データが多く、個数もその都度変化するので、二次元配列に格納しています。 簡易的ではありますが、以下のようなソースコードになります。 <ソースコード> $dataArray → データが格納されている二次元配列 $columns = array(':id', ':name'); → プレイスホルダー名を格納している配列 for($i = 0; $i < count($dataArray); $i++) { $stt = $dataBase -> prepare('insert into tb_test(id, name) values(:id, :name)'); for($j = 0; $j < count($dataArray[ i ]); $j++) { $stt -> bindValue($columns[ $j ] ,$dataArray[ $i ][ $j ]); } $stt -> execute(); } 二次元配列の1行分のデータをループで bindValue() してから execute() で実行しています。 これを二次元配列の行数回繰り返しています。 apacheのエラーログには特に何も表示されていませんが、 登録処理ができません。 このような forループ では実行できないのでしょうか?

    • 締切済み
    • PHP
  • PHPによるXML作成について

    PHPによるXML作成について いつもお世話になっております。 XMLファイルの署名の作成を行っております。 A.xmlというXMLファイルに後で作成した部分をまとめて追記したいと思っています。 「まとめて」というのは、後で追記する部分をプログラム上で一つの変数に格納して、その変数に格納した内容(複数行)を指定した場所に書き込むということです。 XMLの操作として、PHPにこういう機能が無いのであればファイル操作としてでもかまいません。 何かうまく行く方法はありませんでしょうか? よろしくお願いいたします。 実際の例は以下のような感じです。 【A.xmlの例】 <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="00001.xsl" type="text/xsl"?> <DataRoot> <A>999000000000000009</A> <B>0001</B> <STYLESHEET>999000000000000009.xsl</STYLESHEET> <C ID="CCCCCC"> <D> <E> <E1>100495</E1> <E2>4950000020161F01</E2> </E> <F>ファイル名称</F> </D> </C> ******** ここの一括で作成した部分を追記したい ******** </DataRoot> 【追記情報 この部分を変数に格納しています。】 <G xmlns = "http://www.xxxxxxxxx" id = "2009010101"> <Hinfo> <I> <I1></I1> <I2></I2> </I> </Hinfo> </G> 【結果のA.xml】 <?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet href="00001.xsl" type="text/xsl"?> <DataRoot> <A>999000000000000009</A> <B>0001</B> <STYLESHEET>999000000000000009.xsl</STYLESHEET> <C ID="CCCCCC"> <D> <E> <E1>100495</E1> <E2>4950000020161F01</E2> </E> <F>ファイル名称</F> </D> </C> <G xmlns = "http://www.xxxxxxxxx" id = "2009010101"> <Hinfo> <I> <I1></I1> <I2></I2> </I> </Hinfo> </G> </DataRoot>

    • ベストアンサー
    • PHP