┌─────────┐
│PerlでXMLを使う │
│(2)XMLパーサの基本│
│2005/03/21 │
└─────────┘
●XMLパーサの基本
◆パーサとは
・これからXMLデータをPerlで扱うわけですが、「XMLデータをPerlプログラムで読
み書きする」とは何を意味しているでしょうか。
・例えば前回[(1)まずは使ってみる]使用したXMLデータをもう一度見てみましょう。
[リスト1. XMLデータ例]
┌───────────────────────────────────┐
<NameList>
<Member>
<Mail>itiro@hoge.co.jp</Mail>
<Name>Ichiro Suzuki</Name>
<Tel>090-1111-1111</Tel>
</Member>
<Member>
<Mail>jiro@hoge.co.jp</Mail>
<Name>Jiro Sato</Name>
<Tel>090-2222-2222</Tel>
</Member>
<Member>
<Mail>sabro@hoge.co.jp</Mail>
<Name>Sabro Yamada</Name>
<Tel>090-3333-3333</Tel>
</Member>
</NameList>
└───────────────────────────────────┘
・このデータをXML::Simpleパッケージが持つ「XMLin()」関数で読み込みました。
戻り値は構造化されたハッシュデータのリファレンスでした。つまり上記のデー
タを文法的に解釈(parse)し、結果をPerlのデータ構造に変換したのです。
・このように、何らかのデータをプログラムで利用するために、対象データを文法
的/構造的に解釈するプログラムをパーサ(parser)と呼びます。
◆データを渡す方法
・一般にパーサがプログラムにデータを渡す方法は、大きく分けて2種類あります。
(1)イベント方式
(2)オブジェクト方式
・イベント方式とは、「ストリーム」としてデータを読み込んでいる最中、ある構
造/形式毎にイベントを駆動する方式です。
・XMLパーサにおけるイベントは
+ 要素開始タグに出会った
+ 要素終了タグに出会った
+ 通常の文字列データ
等があります。これらのイベントを検知するたびに、それぞれのイベントを処理
する関数(ハンドラ)がパーサから呼び出されます。
・オブジェクト方式の場合、最初にXMLデータ全体を読み込み、構造化されたデー
タとしてプログラムに渡します。
・渡されたデータ構造体は永続的なオブジェクトとして扱うことができます。前回
紹介した XML::Simple の XMLin() はオブジェクト方式に相当します。
◆イベント方式とオブジェクト方式の比較
・どちらのタイプのパーサを使用すべきかについては、扱うXMLデータの構造や大
きさによって適宜選ぶことになります。
・データが比較的小さいのであれば、オブジェクト方式を採用する方が便利です。
繰り返し参照や、最初のデータを見ながらの処理といった融通が利きます。
・しかしデータが大きい場合、オブジェクト方式ではメモリ資源を大量に消費し、
動作速度も目に見えて落ちてきます。このような場合にはイベント方式で、必要
なデータだけをスタックしながら処理する方が効率的です。
・またデータの前後関係が重要な場合にもイベント方式が適しています。
●XML::Parser(イベント方式)
◆XML::Parserについて
・XML::ParserはPerlで初期に開発されたXML用のパーサです。XML::ParserのI/Fは
標準化(*1)されたものではありませんが、ほぼ全てのOS/プラットフォーム用
Perl環境において、使用できる可能性の高いmoduleです。
・XML::Parserは複数のデータ処理スタイルを持っており、イベント方式とオブジ
ェクト方式の両方に対応していますが、今回はイベント方式でXML::Parser利用
してみたいと思います。
◆XML::Parserを使ってみる
・詳細はPerlのDocumentに書いてあるので、ここでは必要な事柄だけ説明します。
使用にあたり意識することは下記2点です。
+ XML::Parserオブジェクトの生成とハンドラの指定
+ 読み込みファイルの指定
・リスト2に具体例を示します。この例では、リスト1のXMLデータをハッシュデー
タへ取り込みます。
[リスト2. XML::Parser使用例]
┌───────────────────────────────────┐
#! perl -w
use XML::Parser;
my %ghData; # データ全体を保持するハッシュ
my $gString; # 文字データ蓄積用
my %ghEle; # 要素毎のハッシュデータ蓄積用
#====================================================================
{
my $obj_parse = XML::Parser -> new (
Handlers => {
Start => \&HandleStart, # タグ開始
End => \&HandleEnd, # タグ終了
Char => \&HandleChar # 通常データ
}
);
$obj_parse -> parsefile($ARGV[0]);
for (my $i=0; $i<=$#{$ghData{Member}}; $i++) {
print "Member : $i\n";
foreach my $ele (keys(%{$ghData{Member}->[$i]})) {
print " $ele $ghData{Member}->[$i]{$ele}\n";
}
}
exit(0);
}
#====================================================================
sub HandleStart {
my ($expat, $element) = @_;
if ($element eq 'Member') { # Memberタグなら要素ハッシュ初期化
%ghEle = (Mail=>'', Name=>'', Tel=>'');
}
$gString = ''; # 文字データも初期化
}
#====================================================================
sub HandleEnd {
my ($expat, $element) = @_;
if ($element eq 'Member') {
my %Ele = %ghEle; # 要素データをローカルハッシュにコピー
push(@{$ghData{Member}}, \%Ele); # リファレンスをプッシュ
}
elsif ($element eq 'Mail') {
$ghEle{Mail} = $gString;
}
elsif ($element eq 'Name') {
$ghEle{Name} = $gString;
}
elsif ($element eq 'Tel') {
$ghEle{Tel} = $gString;
}
}
#====================================================================
sub HandleChar {
my ($expat, $string) = @_;
$gString .= $string;
}
# EOF
└───────────────────────────────────┘
┌───────────────────────────────────┐
[出力結果]
Member : 0
Tel 090-1111-1111
Mail itiro@hoge.co.jp
Name Ichiro Suzuki
Member : 1
Tel 090-2222-2222
Mail jiro@hoge.co.jp
Name Jiro Sato
Member : 2
Tel 090-3333-3333
Mail sabro@hoge.co.jp
Name Sabro Yamada
└───────────────────────────────────┘
・まずXML::Parserの使用宣言を記述します。
use XML::Parser;
・次にオブジェクトの生成と、ハンドラの指定を行います。
my $obj_parse = XML::Parser -> new (
Handlers => {
Start => \&HandleStart, # タグ開始
End => \&HandleEnd, # タグ終了
Char => \&HandleChar # 通常データ
}
);
+ Startはタグ開始時のハンドラ用関数指定です。関数名の参照を渡します。
+ Endはタグ終了時のハンドラ指定です。
+ Charは通常の記述データを読込んだときのハンドラ指定です。
・XML::Parserの通常データ用ハンドラ呼び出しは独特の癖があります。それは
<data>
あいうえお <--- Charハンドル呼び出し
かきくけこ <--- Charハンドル呼び出し
さしすせそ <--- Charハンドル呼び出し
</data>
のように、文字データ内の改行を読む度にCharハンドラ関数が呼び出されてしま
う点です。これは要注意です。
・各ハンドラでは引数指定が
my ($expat, $element) = @_;
のようになっています。第一引数の $expat は Expatのオブジェクト参照を受け
ています。
・ExpatはXML::Parserの裏で動いている低レベルのパーサです。Expatのオブジェ
クトを受けているということは、Expatが持つメソッドを使用できることを意味
しています。例えば
$expat->current_line(); # XMLデータ内の現在行を表示
のように使用します。Expat各メソッド詳細はPerlのDocumentを参照下さい。
・残念ながらXML::ParserにはXMLを書き出すメソッドがありません。ただしデータ
が構造化されていれば、書き出しは難しくないので、自分でルーチンを書いても
良いし、他のパッケージ/クラスを使うのもありです。
●次は
・XML::ParserはPerlで最もよく利用されているパーサですが、そのI/Fが標準とは異
なります。
・そこで、次回はSAX/DOMを利用した環境を検討してみたいと思います。
(*1)XMLで標準化されたI/FにはSAXとDOMがあります。SAXはイベント方式、DOMはオブジ
ェクト方式です。
SAX : Simple Api for XML
DOM : Document Object Model
PerlからSAX/DOMでXMLにアクセスする方法もいずれ紹介したいと思います。
[▲Perl Home▲]
Copyright(c)2005 Monpe
All Rights Reserved