perlでのxmlの勉強中です。

以前も同じような質問をさせて頂いのですが、
今回は以下のXMLデータから
http://live-e.naist.jp/data/getLatestDataAll/
属性temperatureを取得して、
属性のjptimeと
親ノードの属性のlatitude、longitude
を取得し、CSVに保存するプログラムを作成していただけいないでしょうか。


親ノード、属性、子ノード、属性のアクセスの仕方の勉強がしたいので宜しくおねがいいたします。

環境:windows xp、active perl
表示文字コード jis,
エディターは秀丸でプログラムを書く文字コードはeucです。
汎用性の高い出力結果でおねがいいたします。

回答の条件
  • 1人3回まで
  • 13歳以上
  • 登録:2010/07/23 21:02:46
  • 終了:2010/07/25 20:24:58

回答(2件)

id:meeker-bot No.1

ゆう回答回数16ベストアンサー獲得回数62010/07/24 13:20:53

ポイント82pt

この要件であれば、XPathを使用すると良いと思います。

XML::XPathというモジュールを使いますが、

インストールしてないようであれば、Perl Package Managerでインストールしてください。

1つのsensorGroupは、sensorTypeがTemperatureのsensor要素を複数持つことがあるので、二重ループになります。

詳細はソースのコメントを参照してください。

#!/usr/bin/perl
use strict;
use LWP::Simple;
use URI;
use XML::XPath;

#xmlを取得
my $uri = URI->new('http://live-e.naist.jp/data/getLatestDataAll/');
my $response_string  = get($uri);

#XPathオブジェクトを作成
my $xp = XML::XPath -> new( xml => $response_string );

#出力ファイルの定義
open(OUT, ">data.csv");

#sensorType属性にTemperatureを持つsensorGroup要素を取得する
foreach my $node ( $xp -> find('/sensorGroup[@class="collection"]/sensorGroup[sensor[@sensorType="Temperature"]]') -> get_nodelist ){
	my $id = $node -> findvalue('@id');
	my $latitude = $node -> findvalue('@latitude');
	my $longitude = $node -> findvalue('@longitude');
	
	#sensorType属性にTemperatureを持つsensor要素を取得する
	foreach my $nodeInner ( $node -> find('sensor[@sensorType="Temperature"]') -> get_nodelist ){
		my $sensorid = $nodeInner -> findvalue('@id');
		my $jptime = $nodeInner -> findvalue('value/@jptime');
		
		#以下の形式でCSV出力する
		#センサーグループID,緯度,経度,センサーID,日本時間
		print OUT $id . "," . $latitude . "," . $longitude. "," . $sensorid . "," . $jptime . "\n";
	}
}
id:yoshi_3150

ありがとうございました。

xpathというものを利用すると、かなり多くのことができるみたいですね。

みなさん、perlのxml処理をどんな本とか、HPみて勉強しているのでしょうか。

ジュンク堂で本をあさって見ても、あまり詳細な記述は載ってなかったので。

2010/07/25 20:23:08
id:windofjuly No.2

うぃんど回答回数2625ベストアンサー獲得回数11492010/07/24 22:10:57

ポイント82pt
#!/usr/bin/perl
#
# あまり原型をとどめていませんが前回質問 http://q.hatena.ne.jp/1279820516 を改造する形で対応
# 出力は指定によりsjisからjisに変更
# 時刻表記は正規表現を用いて少し整形
# ソート処理やXML形式での出力を行う場合は s1 の結果を直接printするのではなく配列変数に入れてsortしてから出力する必要あり
# 要素が複数の場合は$key2を出力しているが、間違いであれば$key1(親のid)に変更が必要
#
use strict;
use warnings;
use LWP::Simple;
use Encode;
use XML::Simple;
#
sub s1 {
    ( my $temp1, my $temp2 ) = @_;
    $temp2 =~ s|^(\d+)-(\d+)-(\d+)T((\d+):(\d+):(\d{2})).*$|$1/$2/$3 $4|;
    return $temp1 . ',' . $temp2 . "\n";
}
#
my $readXML = XML::Simple -> new -> XMLin( get( 'http://live-e.naist.jp/data/getLatestDataAll/' ), SuppressEmpty => '' );
foreach my $key1 ( keys %{ $readXML->{ 'sensorGroup' } } ) {
    if ($key1 ne '') {
        my $ref1 = $$readXML{ 'sensorGroup' }{ $key1 };
        if ( $$ref1{ 'sensor' }{ 'sensorType' } ) {
            # 要素が1つの場合
            if ( ( $$ref1{ 'sensor' }{ 'sensorType' } eq 'Temperature' ) && ( $$ref1{ 'value' }{ 'jptime' } ) ) {
                print s1( '"' . encode( 'jis', $key1 ) . '"' . ',' . $$ref1{ 'latitude' } . ',' . $$ref1{ 'longitude' }, $$ref1{ 'value' }{ 'jptime' } );
            };
        } elsif ( $$ref1{ 'sensor' } ) {
            # 要素が複数の場合
            foreach my $key2 ( keys %{ $$ref1{ 'sensor' } } ) {
                my $ref2 = $$ref1{ 'sensor' }{ $key2 };
                if ( ( $$ref2{ 'sensorType' } ) && ( $$ref2{ 'value' }{ 'jptime' } ) ) {
                    if ( $$ref2{ 'sensorType' } eq 'Temperature' ) {
                        print s1( '"' . encode( 'jis', $key2 ) . '"' . ',' . $$ref1{ 'latitude' } . ',' . $$ref1{ 'longitude' }, $$ref2{ 'value' }{ 'jptime' } );
                    }
                }
            }
        }
    }
}
id:yoshi_3150

連答ありがとうございました。

simpleだとこのようになるんですね。

2010/07/25 20:24:11

コメントはまだありません

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

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

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

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません