XML::LibXML の SAX インタフェースで XML をパースする

libxml2 を C でいじっていたんですけど,あれこれ書いていくのが面倒になったので,とりあえず Perl でプロトタイプを書くことにしました。

つうことで,Perl から XML::LibXML を使ってみました。前回と同じく SAX インタフェースです。普通の用途では使わないです。

はじめのいっぽ

use strict;
use warnings;

package MySAXHandler;

use base qw( XML::SAX::Base );

sub start_element {
    my ($self, $el) = @_;

    printf "<%s>\n", $el->{Name};
}

package main;

my $parser
    = do {
        use XML::LibXML::SAX;

        XML::LibXML::SAX->new();
    };

my $source = do { local $/; <> };   # slurp!

$parser->parse_string($source, Handler => MySAXHandler->new());

んーやっぱ LL だと楽ですね。無駄に do { } ブロックで囲んでいますが,あまり意味はありません。

手順は,

  1. XML::SAX::Base を継承した SAX イベントハンドラクラスを作る
  2. XML::LibXML::SAX*1インスタンスを生成する
  3. ハンドラとして前述のハンドラクラスのインスタンスを指定してパースする
    • パーサの生成時にハンドラを指定しておくこともできます

実行すると,

% perl sax.pl test.xml

<html>
<head>
<body>
<div>
<p>
<div>
<span>
<span>
<ul>
<li>

無事 start_element(ns) のイベントをとらえることができました。

もうちょっと柔軟性をもたせる

さきほど XML::SAX::Base というのがでてきましたが,XML::LibXML::SAXXML::SAX という Perl における SAX インタフェース仕様を満たしているのです。XML::LibXML::SAX 以外にもこの仕様を満たすモジュールはいくつかあります。

なので,よりお行儀良く書くには,XML::SAX に用意されているファクトリクラスを経由してパーサを取得したほうがよいです。

my $parser
    = do {
        use XML::SAX::ParserFactory;

        XML::SAX::ParserFactory->parser();
    };

どのパーサクラスを使うかは XML::SAX::ParserFactory が自動的に判断してくれます。パーサ取得時に RequiredFeatures にパーサとしての必要条件を指定したり,SAX.ini でパーサを指定することもできます。

もしプログラム側で XML::LibXML::SAX をパーサとして使うことを強制したければ,

my $parser
    = do {
        use XML::SAX::ParserFactory;

        local $XML::SAX::ParserPackage = 'XML::LibXML::SAX';

        XML::SAX::ParserFactory->parser();
    };

のように $XML::SAX::ParserPackage に設定する方法もあります。


とまあいろいろ書いてきましたが,捨てスクリプトとか書く際は,直接 XML::LibXML::SAX インスタンスを生成して構わないです。

おまけ

これだけだと内容がないよーなので,CPAN モジュールで XML の「パース」に使われるモジュールを図示してみました。ただし XML::RSS のように目的に特化したモジュールは除きます。


  • 上のほうほど高位な API です。
  • かつては低位 API としては expat ライブラリを使った XML::Parser モジュールがメジャーだったんですが,expat より SAX2 インタフェースのほうができることが多く新しいので,今ライブラリを書くとすれば XML::Parser より XML::SAX を利用したほうがよいでしょう。より高位な XML::LibXML(の DOM インタフェースや XPath インタフェース)をベースとして利用するのも手です。
  • かつて(と書くと失礼か)Xerces という SAX インタフェースと DOM インタフェースを兼ね備えた C++ ライブラリがありました。これを使用する XML-Xerces という Perl モジュールもあるのですが,SWIG によるラッパをかぶせてあるだけでドキュメントも不足してますし,Xerces より libxml のほうが機能が多いので,libxml ベースの XML::LibXML を使うほうがよいでしょう。
  • 図では省略しましたが XML::SAX を下位で利用している XML::DOM2 という DOM2 インタフェースモジュールもあります。ただ,あまり使われていないようです*2。現時点では素直に XML::LibXML (ry

*1:XML-LibXML には XML::LibXML::SAX::Parser というクラスもあるんですが,こちらは一度 DOM ツリーを構築したドキュメントから SAX イベントを発生させるものだと思います。

*2:もしこなれているのであれば XML::SAX をベースに使っているぶん,より柔軟なアーキテクチャなのですが。