現在、PHP4環境にて、XML_unserialize関数を使用したXMLパーサ処理を作っています。

具体的には、沿線データ(API)を取得し、表示したい…といったものです。

以下に作りかけのソースを添付します。(コメント欄参照)
おおまかな流れとして、APIから抽出されたXMLデータをアンシリアライズした後、
変数$dataにセット、その後、各ノードに格納された値を取得し変数にセットしています。

「完全一致」の場合は動くのですが、例えば複数のクエリーが返される「横浜」など
で検索すると、検索結果には何も表示されなくなってしまいます。
参考→http://www.ekidata.jp/api/n.php?w=%e6%a8%aa%e6%b5%9c

理想の動きとしては、以下の様に複数行を表示したいのですが、どのようにプログラムを変更すれば宜しいでしょうか?
PHPの配列操作が初心者のため、参考サイトを見ながら苦戦しています…

宜しければ御回答をよろしくお願い致します。

回答の条件
  • URL必須
  • 1人2回まで
  • 登録:
  • 終了:2009/06/06 08:49:21
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:kn1967 No.2

回答回数2915ベストアンサー獲得回数301

ポイント100pt

配列の構造としては下記のようになります

■1件の場合

Keith Devens .com - XML to PHP translator

Array
(
    [ekidata attr] => Array
        (
            [version] => ekidata.jp station name list api 1.0
        )
    [ekidata] => Array
        (
            [coord] => Array
                (
                    [word] => 陸奥横浜
                    [total] => 1
                    [error] => 0
                )
            [station] => Array
                (
                    [line_cd] => 11203
                    [line_name] => JR大湊線(はまなすベイライン大湊線)
                    [station_cd] => 1120305
                    [station_g_cd] => 1120305
                    [station_name] => 陸奥横浜
                    [pref_cd] => 2
                    [lon] => 141.249757
                    [lat] => 41.086544
                )
        )
)

■複数件の場合

Keith Devens .com - XML to PHP translator

(本当は[15]まであるのだけれども長くなるので省略してあります)

Array
(
    [ekidata attr] => Array
        (
            [version] => ekidata.jp station name list api 1.0
        )
    [ekidata] => Array
        (
            [coord] => Array
                (
                    [word] => 横浜
                    [total] => 16
                    [error] => 0
                )
            [station] => Array
                (
                    [0] => Array
                        (
                            [line_cd] => 11203
                            [line_name] => JR大湊線(はまなすベイライン大湊線)
                            [station_cd] => 1120305
                            [station_g_cd] => 1120305
                            [station_name] => 陸奥横浜
                            [pref_cd] => 2
                            [lon] => 141.249757
                            [lat] => 41.086544
                        )
                    [1] => Array
                        (
                            [line_cd] => 11301
                            [line_name] => JR東海道本線(東京~熱海)
                            [station_cd] => 1130105
                            [station_g_cd] => 1130105
                            [station_name] => 横浜
                            [pref_cd] => 14
                            [lon] => 139.622704
                            [lat] => 35.466195

                        )
                )

        )

)

上記を比較すれば理解しやすいかと思いますが

同じ「陸奥横浜」でも

$data[ekidata][station][station_name]

$data[ekidata][station][0][station_name]

という具合に入っている階層が違いますので取り出し方も当然違いが出てきます。


対処方法として簡単な方法としては

件数が $data[coord][total] に入っていますから、この値を使って

1件の場合はそのまま表示処理に進み、

複数件の場合は[total]の値を使ってループ処理で駅名一覧を作成・表示して

駅名を選択させるなどといった対応にすれば良いでしょう。

(入っている数値は16だけど、配列の通し番号はゼロから始まりますので

 -1して15で終わるように、処理の際には注意してくださいね)


なお、

※コメント欄を表示する設定にしておかないと

 自分で書き込んだコメントも表示されません。

※php5にするようにという注意だけの書き込みはどうかと思うけど

 出来るならばバージョンアップする事をお勧めはします。

id:takupon2009

コメントありがとうございます!

自分なりに、色々と弄っていたのですが、配列の場合だけ

Arrayと結果が返ってきてしまいます…。

因みに、

include('xml.php');

$keyword = $_GET['keyword']; (フォームからGETで値を取得しています)

// API情報の取得

$xml_data = file_get_contents("http://www.ekidata.jp/api/n.php?w=$keyword");

$data = XML_unserialize($xml_data);

// 必要な情報をピックアップして取得、変数に代入

$word = print_r($data["ekidata"]["coord"]["word"],TRUE); // 検索ワード

$total = print_r($data["ekidata"]["coord"]["total"],TRUE); // 検索結果

$station_nm = print_r($data["ekidata"]["station"]["station_name"],TRUE); // 駅名

$line_nm = print_r($data["ekidata"]["station"]["line_name"],TRUE); // 沿線名

// 文字コンバート

各変数はmb_convert_encodingで文字コードを変換する(省略)

// 配列に入っていた場合…

foreach( $data["ekidata"]["station"] as $key => $value )

{

echo $key. ":" .$value."
\n";

}

まだ続くのですが、最後の「配列に入っていた場合」の処理ですが、

0:Array

1:Array

2:Array...

と続き、配列は取得できているものの、値が表示できていません。

これは配列に値がしっかりと代入されていないということなのでしょうか…?

2009/06/04 19:30:15

その他の回答3件)

id:QuestR2 No.1

回答回数435ベストアンサー獲得回数13

ポイント30pt

PHP5の関数をPHP4で使う - PHP_Compatを使ったほうがよいと思います。

http://techblog.ecstudio.jp/tech-tips/php_compat.html

id:takupon2009

コメントありがとうございます!

PEARのPHP_Compatは以前、検討したことがあります。

しかし、作りこみを全てPHP4でやってしまったので、出来ればPHP4の関数で

使用したいという気持ちがありまして…。 ギリギリまで頑張ってみます!

2009/06/04 19:23:54
id:kn1967 No.2

回答回数2915ベストアンサー獲得回数301ここでベストアンサー

ポイント100pt

配列の構造としては下記のようになります

■1件の場合

Keith Devens .com - XML to PHP translator

Array
(
    [ekidata attr] => Array
        (
            [version] => ekidata.jp station name list api 1.0
        )
    [ekidata] => Array
        (
            [coord] => Array
                (
                    [word] => 陸奥横浜
                    [total] => 1
                    [error] => 0
                )
            [station] => Array
                (
                    [line_cd] => 11203
                    [line_name] => JR大湊線(はまなすベイライン大湊線)
                    [station_cd] => 1120305
                    [station_g_cd] => 1120305
                    [station_name] => 陸奥横浜
                    [pref_cd] => 2
                    [lon] => 141.249757
                    [lat] => 41.086544
                )
        )
)

■複数件の場合

Keith Devens .com - XML to PHP translator

(本当は[15]まであるのだけれども長くなるので省略してあります)

Array
(
    [ekidata attr] => Array
        (
            [version] => ekidata.jp station name list api 1.0
        )
    [ekidata] => Array
        (
            [coord] => Array
                (
                    [word] => 横浜
                    [total] => 16
                    [error] => 0
                )
            [station] => Array
                (
                    [0] => Array
                        (
                            [line_cd] => 11203
                            [line_name] => JR大湊線(はまなすベイライン大湊線)
                            [station_cd] => 1120305
                            [station_g_cd] => 1120305
                            [station_name] => 陸奥横浜
                            [pref_cd] => 2
                            [lon] => 141.249757
                            [lat] => 41.086544
                        )
                    [1] => Array
                        (
                            [line_cd] => 11301
                            [line_name] => JR東海道本線(東京~熱海)
                            [station_cd] => 1130105
                            [station_g_cd] => 1130105
                            [station_name] => 横浜
                            [pref_cd] => 14
                            [lon] => 139.622704
                            [lat] => 35.466195

                        )
                )

        )

)

上記を比較すれば理解しやすいかと思いますが

同じ「陸奥横浜」でも

$data[ekidata][station][station_name]

$data[ekidata][station][0][station_name]

という具合に入っている階層が違いますので取り出し方も当然違いが出てきます。


対処方法として簡単な方法としては

件数が $data[coord][total] に入っていますから、この値を使って

1件の場合はそのまま表示処理に進み、

複数件の場合は[total]の値を使ってループ処理で駅名一覧を作成・表示して

駅名を選択させるなどといった対応にすれば良いでしょう。

(入っている数値は16だけど、配列の通し番号はゼロから始まりますので

 -1して15で終わるように、処理の際には注意してくださいね)


なお、

※コメント欄を表示する設定にしておかないと

 自分で書き込んだコメントも表示されません。

※php5にするようにという注意だけの書き込みはどうかと思うけど

 出来るならばバージョンアップする事をお勧めはします。

id:takupon2009

コメントありがとうございます!

自分なりに、色々と弄っていたのですが、配列の場合だけ

Arrayと結果が返ってきてしまいます…。

因みに、

include('xml.php');

$keyword = $_GET['keyword']; (フォームからGETで値を取得しています)

// API情報の取得

$xml_data = file_get_contents("http://www.ekidata.jp/api/n.php?w=$keyword");

$data = XML_unserialize($xml_data);

// 必要な情報をピックアップして取得、変数に代入

$word = print_r($data["ekidata"]["coord"]["word"],TRUE); // 検索ワード

$total = print_r($data["ekidata"]["coord"]["total"],TRUE); // 検索結果

$station_nm = print_r($data["ekidata"]["station"]["station_name"],TRUE); // 駅名

$line_nm = print_r($data["ekidata"]["station"]["line_name"],TRUE); // 沿線名

// 文字コンバート

各変数はmb_convert_encodingで文字コードを変換する(省略)

// 配列に入っていた場合…

foreach( $data["ekidata"]["station"] as $key => $value )

{

echo $key. ":" .$value."
\n";

}

まだ続くのですが、最後の「配列に入っていた場合」の処理ですが、

0:Array

1:Array

2:Array...

と続き、配列は取得できているものの、値が表示できていません。

これは配列に値がしっかりと代入されていないということなのでしょうか…?

2009/06/04 19:30:15
id:pahoo No.3

回答回数5960ベストアンサー獲得回数633

ポイント40pt

これは配列に値がしっかりと代入されていないということなのでしょうか…?

いいえ、違います。

配列の内容は echo で表示させることができないからです。


最後の foreach ループですが、$value そのものが連想配列になっています。関数 print_r を使って下記のようにすれば、取得したデータ構造がわかりやすくなると思います。

foreach( $data["ekidata"]["station"] as $key => $value ) {
    echo $key . ' : ';
    print_r($value);
    echo "\n";
}
id:takupon2009

ご回答ありがとうございます!

よくよく考えてみれば、確かに見当違いをしていたようです。。

ご指摘のように、print_rで配列を調べ、結果として以下のように書き直しました。

foreach( $data["ekidata"]["station"] as $key => $value )

{

$station_nm = print_r($data["ekidata"]["station"][$key]["station_name"],TRUE);

$line_nm = print_r($data["ekidata"]["station"][$key]["line_name"],TRUE);

echo $station_nm."駅(".$line_nm.")
";

echo "\n";

}

$keyには配列数が入ってくるので、その数をprint_r~の中にセットし、取得してみました。

すると、見事に一覧で取得することができました!

が・・・。

ここで不思議な現象が発生してしまいまして、

結果が以下のようになります。

「京急蒲田」での検索結果は2件です。

莠ャ諤・闥イ逕ー駅(莠ャ諤・譛ャ邱・

莠ャ諤・闥イ逕ー駅(莠ャ諤・遨コ貂ッ邱・

本来であれば、

「京急蒲田」での検索結果は2件です。

京急蒲田(京急本線)

京急蒲田(京急空港線)

のように表示してくれるハズなのですが、なぜか文字化けします。

文字化け対策として、mb_convert_encodingで適切な文字コードに変換しているのですが、

これまた不思議なことに、検索結果が複数返ってくる処理だけ、文字化けしてしまうのです。

完全一致での検索結果では、同じようにmb_convert_encodingで文字コードを変換して

成功しているので、このようなことはありえないと思うのですが…

念のため、echo mb_detect_encoding関数で文字列のエンコードを調べましたが、

SJISと出ます。 プログラム内ではmb_convert_encoding($total, "SJIS-win", "auto")

といった感じで、文字コードを変換しています。(この変換方法で完全一致の処理結果は成功しています)

if ($total == "1")

  {

    $station_nm = print_r($data["ekidata"]["station"]["station_name"],TRUE); // 駅名

    $line_nm = print_r($data["ekidata"]["station"]["line_name"],TRUE); // 沿線名

    $station_nm = mb_convert_encoding($station_nm, "SJIS-win", "auto"); // 変換

    $line_nm = mb_convert_encoding($line_nm, "SJIS-win", "auto"); // 変換

  }

else if ($total == "0")

  {

    echo "「".$word."」という駅は見つかりませんでした。
";

  }

else

  {

    echo "「".$word."」での検索結果は".$total."件です。
";

    $station_nm = "";

    $station_nm = print_r($data["ekidata"]["station"]["station_name"],TRUE); // 駅名

    $line_nm = print_r($data["ekidata"]["station"]["line_name"],TRUE); // 沿線名

    $station_nm = mb_convert_encoding($station_nm, "UTF-8", "auto"); // 変換

    $line_nm = mb_convert_encoding($line_nm, "SJIS-win", "auto"); // 変換

foreach( $data["ekidata"]["station"] as $key => $value )

  {

    $station_nm = print_r($data["ekidata"]["station"][$key]["station_name"],TRUE); ←なぜか文字化け

    $line_nm = print_r($data["ekidata"]["station"][$key]["line_name"],TRUE); ←なぜか文字化け

    echo $station_nm."駅(".$line_nm.")
";

    echo "\n";

  }

  }

elseの処理で入ってくる変数は初期化をしていませんが、これが関係しているのかと思い、

変数を一度初期化した後に、再度値を入れなおして見ましたが変化がありません。

このPHPは文字コードSJISで保存しています。

サーバのPHP.iniのmb_stringは有効で、以下のような設定となっています。

[mbstring]

default_charset = "Shift_JIS"

mbstring.language = Japanese

mbstring.internal_encoding = SJIS

mbstring.http_input = pass

mbstring.http_output = pass

mbstring.encoding_translation = Off

mbstring.detect_order = SJIS,JIS,UTF-8,EUC-JP,ASCII

mbstring.substitute_character = none;

何か手がかりになるような情報を探してはいますが、文字化けだけが解消できず苦戦しています。

あと少しで完成しそうなのですが…(涙)

2009/06/04 23:57:06
id:pahoo No.4

回答回数5960ベストアンサー獲得回数633

ポイント40pt

何か手がかりになるような情報を探してはいますが、文字化けだけが解消できず苦戦しています。

失礼ながら、多次元連想配列の扱いと、関数 print_r の使い方が間違っているようです。

foreach で回しながら文字コードを変換していくのがいいでしょう。下記にサンプル・スクリプト(一部)を掲げます。

foreach ($data['ekidata']['station'] as $station) {
    $station_nm = mb_convert_encoding($station['station_name'], 'SJIS-win', 'UTF-8');
    $line_nm = mb_convert_encoding($station['line_name'], 'SJIS-win', 'UTF-8');
    echo $station_nm . '駅(' . $line_nm . ')';
    echo "\n";
}

なお、mb_convert_encoding で 'auto' を使うと変換ミスを起こすケースがあるので、入力は 'UTF-8' で決め打ちにしました。

id:takupon2009

御回答ありがとうございます!

kn1967氏の模範解答と共に、foreachの中での変換処理について、対策を施してみました。

無事に文字化けは解消されました。 もう少し書籍やネットなどで情報を探して勉強してみようと思います!

お忙しい中、御回答ありがとうございました!

2009/06/06 08:45:13
  • id:kn1967
    >配列の場合だけ
    >Arrayと結果が返ってきてしまいます…。

    私の回答は、それを説明しています。
    ■1件だけの場合
      $data[ekidata][station][station_name]
    ■複数件の場合
      $data[ekidata][station][0][station_name]
    という部分が、その理由を示しているのですが説明が判り辛かったでしょうか。

    >配列は取得できているものの、値が表示できていません。

    1件の場合であれば
    foreach( $data["ekidata"]["station"] as $key => $value )
      print 出力
    }
    で良いのですが
    複数件の場合は通し番号が必要になりますから
    for($bango = 0; $bango < $data["ekidata"]["coord"]["total"]; $bango++) {
      foreach( $data["ekidata"]["station"][$bango] as $key => $value )
        print 出力
      }
    }
    というような形になります。

    1件の場合でも配列に替えてしまうとか、
    サブルーチン化して統一性を出す方法などもありますが
    あまり手を伸ばすと後が結構つらくなるので、とりあえず
    $data["ekidata"]["coord"]["total"]の値が
    「ゼロ」か「1」か「1以上」かの3パターンに分類して
    それぞれ処理を分けるのが判りやすいでしょう。
  • id:takupon2009
    takupon2009 2009/06/05 00:03:27
    >kn1967さん

    ご回答ありがとうございます。
    はい、ちょっと勘違いしておりました。

    家に帰って冷静に考えてみると、確かに通し番号がなくては取得できません。
    そのロジックについての説明も考えてみれば納得が行きました。

    別の方の回答にも書いてしまいましたが、
    if条件でtotal件数を元に、ある程度の条件分岐を設けました。
    そのあとに、各々の条件の中で値を取得し、表示させることができました。

    今回は表示だけでなく、form処理に続けたいこともありましたので、
    変数にセットすることが最終的な目標でした。

    そのため、print出力の結果を変数に格納しています。
    そしてその変数を表示するところまではうまく行き、 配列数も問題ありませんでした。

    あとは、文字化けの問題だけが解決できればいいのですが…。
  • id:kn1967
    複数の場合が問題なので else以下 の部分に的を絞って流れを追って見ましょう。

    (1)先頭部分
    echo "「".$word."」での検索結果は".$total."件です。
    これは何も問題ないですね。

    (2)変数格納部分
    $station_nm = "";
    $station_nm = print_r($data["ekidata"]["station"]["station_name"],TRUE); // 駅名
    $line_nm = print_r($data["ekidata"]["station"]["line_name"],TRUE); // 沿線名
    $station_nm = mb_convert_encoding($station_nm, "UTF-8", "auto"); // 変換
    $line_nm = mb_convert_encoding($line_nm, "SJIS-win", "auto"); // 変換
    これらに問題がありますね。通し番号がどこにも入ってませんから、
    $station_nmや$line_nmに駅名を入れる事に失敗しています。

    (3)ループ部分
    foreach( $data["ekidata"]["station"] as $key => $value )
    {
      $station_nm = print_r($data["ekidata"]["station"][$key]["station_name"],TRUE);
      $line_nm = print_r($data["ekidata"]["station"][$key]["line_name"],TRUE);
      echo 出力
    }
    どこで文字変換してますか?
    このループの中に(2)を入れないといけませんよね。

    (4)まとめると・・・
    echo "「".$word."」での検索結果は".$total."件です。
    foreach( $data["ekidata"]["station"] as $key => $value )
    {
      $station_nm = print_r($data["ekidata"]["station"][$key]["station_name"],TRUE); // 駅名
      $line_nm = print_r($data["ekidata"]["station"][$key]["line_name"],TRUE); // 沿線名
      $station_nm = mb_convert_encoding($station_nm, "SJIS-win", "auto"); // 変換
      $line_nm = mb_convert_encoding($line_nm, "SJIS-win", "auto"); // 変換
      echo 出力
    }
  • id:kn1967
    どうしてprint_rを使って変数に格納しているのだろうと考えてしまいましたが
    pahoo氏のechoとprint_rの投稿で頭が混乱なさったのですね。

    pahoo氏の回答は「配列の中身を確認する方法を示している」だけです。
    配列の中身は私の回答中に既に示してある通りで
    受け取る内容は「駅名」や「沿線名」なのですからprint_rは不要です。

    (5)print_r は配列出力用
    echo "「".$word."」での検索結果は".$total."件です。
    foreach( $data["ekidata"]["station"] as $key => $value )
    {
      $station_nm = mb_convert_encoding($data["ekidata"]["station"][$key]["station_name"], "SJIS-win", "auto"); // 変換
      $line_nm = mb_convert_encoding($data["ekidata"]["station"][$key]["line_name"], "SJIS-win", "auto"); // 変換
      echo 出力
    }
    (6)
    if ($total == "1") の部分でも同様にprint_rは不要ですから(5)同様に変更しましょう。
  • id:takupon2009
    takupon2009 2009/06/06 08:47:05
    >kn1967様
    勘違いと混乱をしてまして、自分でも試行錯誤していましたが、
    ようやくprint_rとecho、変数への代入などの違いが分かってきました。
    おかげさまで、作りたかったシステムがはじめて構築することが出来ました!
    たいしたシステムではありませんが、感動しています!!

    本当にありがとうございました!
  • id:pahoo
    「駅データ.jp」を利用し、駅の近くの珈琲店を探すPHPスクリプトを、DOM XML(PHP4)、SimpleXML(PHP5)、PHP XML Library(Keith Devens.com)の3種類の方法で作ったものを「PHPで駅の近くの珈琲店を探す」(http://www.pahoo.org/e-soul/webtech/php06/php06-23-01.shtm)に掲載しました。ご参考まで。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません