XML::LibXMLで(とりあえず)XMLをパースする

これまでXML::TreePPにべったりだった私。最近はXMLの要素指定にXPathを使うのが主流?らしいので、その練習がてら使ってみることにしました。

インストール

検索するとみんな苦労しているインストール。まずはいろんなライブラリを入れないといけない。
僕の環境(Fedora 7 + Perl5.8.8)ではこんな感じでインストール。

$ su -
# yum -y install libxml
# yum -y install libxml-devel
# yum -y install libxml2
# yum -y install libxml2-devel
# yum -y install libxml++
# yum -y install libxml++-devel
# cpan -fi XML::LibXML

なんつーかもう、片っ端から放り込んだ感じですが・・・


サンプルファイル

今回は /home/amaya/ramens.xml という場所に置いてみます。/span>

<Ramens>
    <Ramen>
        <Shop name="Perl軒" />
        <Menus>
            <Menu>野菜ラーメン</Menu>
            <Menu>全部入りラーメン</Menu>
        </Menus>
        <Comment>会社帰りに良く寄るお店</Comment>
    </Ramen>
    <Ramen>
        <Shop name="麺屋CPAN" />
        <Menus>
            <Menu>カレーラーメン</Menu>
            <Menu>餃子</Menu>
        </Menus>
        <Comment>床がベタベタしてる</Comment>
    </Ramen>
</Ramens>

こんな感じで作ったファイルを解析して、整形表示するサンプルスクリプトを作ってみます。
(自分にとって)わかりやすくするために「ノード」という表現は使わず「タグのリスト」と表現します。

XPathの記述

難しいことは良く分からないので、取り急ぎ解析に必要な情報だけ。

  • 基本的にはUNIXのパスと同じ文法。
    • 階層を「/」で表す。現在の階層を示すには「.」で、ひとつ上の階層を示すには「..」。
  • 要素( の「name」の部分)は「@」で表す

サンプルスクリプト

  • ramen_list.pl
#!/usr/bin/perl

use strict;
use warnings;
use XML::LibXML;

my $parser  = XML::LibXML->new();
my $dom     = $parser->parse_file("/home/amaya/ramens.xml");

# ルートノード(<Ramens>)の下にある<Ramen>ノードを取得
my @ramens = $dom->findnodes('//Ramen');

# <Ramen>ノードを展開
foreach my $ramen (@ramens) {

    # <Ramen><Shop name="*">の値を取得
    my $shop_name = $ramen->findvalue('Shop/@name');

    print <<"HEADER";
----------------------------------
Shop: $shop_name
----------------------------------
HEADER

    # <Ramen><Comment>の値を取得
    printf("[Comment] %s\n", $ramen->findvalue('Comment'));

    # <Ramen>の下にある<Menus><Menu>ノードを取得
    my @menus = $ramen->findnodes('Menus/Menu');

    print "[Menu]\n";

    # <Menu>ノードを展開
    foreach my $menu (@menus) {
        # <Menu>ノードを展開
        printf(" - %s\n", $menu->findvalue('.'));
    }
}

print文だらけですみません。

my @elements = $class->findnodes('タグへのXPath')でノード(タグのまとまり)を取得し、さら$elements[0]->findvalue('findnodesで指定したタグ以下の相対的なXPath')という形で、ひとつひとつのタグに含まれる値を取得していきます。

これを実際に走らせると、こんな感じ。

[amaya@localhost ~]$ perl ramen_list.pl

                                                                  • -

Shop: Perl

                                                                  • -

[Comment]
会社帰りに良く寄るお店
[Menu]
- 野菜ラーメン
- 全部入りラーメン

                                                                  • -

Shop: 麺屋CPAN

                                                                  • -

[Comment]
床がベタベタしてる
[Menu]
- カレーラーメン
- 餃子


うーむ。だいたい雰囲気はつかめた感じ・・・?


とりあえず今回はここまで。。
同じ処理を慣れないモジュールでやるのって、機種変直後の携帯をいじるくらいもどかしいですな。
もっとエレガントな方法をご存知の方、いらっしゃいましたらツッコミをお願いします・・・