前回 (参考文献 を参照) では、スクリプト言語、特に Perl を使った XML の操作方法を説明しました。 このようにファイルを解析する過程では、タグが見つかるたびにハンドラーが呼び出されます。そのため、メモリー使用と処理時間の両方の点で、XML を非常に効率よく処理することができます。ただし、イベント・ベースの方法では実行が難しいタスクもあります。たとえば、文書の特定のセグメントを移動したり配置し直したり、文書内で項目をソートしたりする必要があるとします。文書は 1 つのストリームとして処理されるので、項目をソートしたり、配置し直したりする前に、まずコンポーネントを格納しておく必要があります。コンポーネントを自動的に格納するメカニズムがあれば、このようなタスクは極めて簡単になります。
XML 文書は、バランスがよく取れていて、ツリーとして簡単に格納できることが必要です。XML 文書をツリー構造に解析すれば、そのツリーを操作することができます。これにより、文書を非常に柔軟に扱えるようになります。文書のコンポーネントにランダムにアクセスしたり、コンポーネントを配置し直したり、追加/削除したりすることができます。これは特に、処理の流れが XML 文書内の要素の順序やオカレンスではなく、外部ロジックに基づいているアプリケーションに適しています。文書をツリーとして格納しておけば、タグや要素を入れる場所や時間で処理を制御するのではなく、文書のデータや構造にランダムにアクセスすることができます。
しかし、ツリー・ベースの方法にも欠点はあります。XML 文書全体を解析しなければなりません。また、処理とビジネス・ロジックが実行される前 に、ツリー・データ構造を作成しておかなければなりません。ツリー・データ構造は一般にメモリーに格納されるので、この方法は、ストリーム・ベース方法の場合よりも、結果的に大量のメモリーを消費することになります。
Perl を使って XML のツリー・ベース処理を実行するモジュールは、一般的に 4 つあります。それぞれ、目標と歴史が若干異なります。
- DOM
- Grove
- Twig
- XML::Simple
Document Object Model (DOM) は、プラットフォームおよび言語に中立のインターフェースで、XML 文書の内容、構造、スタイルに動的にアクセスおよび更新します。DOM は、文書を表すためのオブジェクトの標準セット、これらのオブジェクトの組み合わせ方法である標準モデル、およびオブジェクトにアクセスおよび操作するための標準インターフェースを提供します。DOM は、W3C 推奨であり、公認 Web 標準です。DOM の実装は、Perl、Tcl、Python、C、C++、Java などのさまざまな言語で利用できます。
Grove は、代替文書モデルです。Grove の概念のルーツは SGML 処理です。リソース (たとえば、XML 文書) をノードとプロパティーのセットとして表現するという概念に基づきます。Grove は、ノードの方向を持ったグラフであり、それぞれのノードは 1 つ以上のプロパティーを表します。これは、アトミック 値 (たとえば、ストリング、ブール、整数など)、ノードのリスト、ノードの参照でも構いません。このように、ノードは、一連のプロパティーから成り立ちます。プロパティーの中には、他のノードへの参照になるものもあります。
Twig は、大きな XML 文書を処理することができる、ツリー・ベースの文書モデルです。処理時に (前に説明したイベント・ベースのモデルのように) コールバックを生成したり、文書内で処理するサブツリーと無視するサブツリーを区別したり、処理済みのサブツリーのフラッシュを実行したりできるようにすることで、Twig では、メモリーのオーバーヘッドを制御し、優れたパフォーマンスを保ちながら、非常に大きなファイルを処理することが可能になります。
XML::Simple の目的は、XML ファイルの処理を Perl で単純かつ自然にすることです。XML ファイルは、Perl のデータ構造に変換されるので、標準の Perl 構文を使用して簡単にデータにアクセスすることができます。本来の目的が構成ファイルの処理である XML::Simple は、限られた XML サブセットしか処理しません。たとえば、マークアップ・テキストなど、テキスト要素とネスト要素の両方を含む XML 要素は処理されません。
4 つのモジュールのどれを使用しても、強力な XML ベースのアプリケーションを作成できることに変わりはありません。どのモジュールが最適かは、アプリケーションと、ユーザーの環境設定および必要条件によって異なります。
処理するデータに複雑な構造が含まれていない場合には、XML::Simple が一番良いでしょう。オーバーヘッドが低く、非常にわかりやすい単純な API により、簡単に覚え、使用し、展開することができます。
データがもっと複雑な場合は、DOM、Grove、および Twig を利用します。DOM は非常に人気があり、長所もたくさんあります。W3C 推奨で、利用できる言語も数多く、さまざまな記事、チュートリアル、書籍で取り上げられています。また、XML::DOM パッケージに含まれている XQL などの他の XML 標準で使用しても支障ありません。しかし、その意図的に言語に中立であるという性質のため、DOM は Perl にあまり似ていないので、Perl を使い慣れているユーザーにはわかりにくいでしょう。DOM は、他のモジュールよりも便利な機能が少ないので、多少不便です。
Grove は、ルーツである SGML から多くの遺産を引き継いでおり、情報を格納するための、よく考え抜かれた、すばらしいモデルを提供します。インターフェースは Perl の構文に自然にマッチし、ビジター・ベースのモデルは、ツリー構造内を走査するための豊富なメソッドを提供します。残念ながら、Grove の開発は他のモジュールに比べてあまり積極的ではなく、人気がありません。
Twig は、使いやすいツールを提供すると同時に、速度とメモリー節約の両方で高いパフォーマンスを発揮します。Grove と同様、Twig のインターフェースは Perl に似ているので、プログラミングが簡単になる便利な機能を提供します。特に大きいファイルを処理する場合は、ツリーのパーツを選択して処理できる点が他のモジュールと異なります。
ここでは、その人気の高さと、標準であるという理由から、DOM を選びました。Xerces-Perl など、DOM の C と C++ の実装が利用できるようになったことから、DOM の地位は一層高まり、パフォーマンスの向上が約束されています。
DOM は、ノードのツリーです。XML 文書の各部分は、ツリー内でノードとして表されます。これは、想像していたのと少し異なるかもしれません。たとえば、属性は、Perl ではハッシュとして表現できました。DOM では、属性、XML タグ、テキストの内容、およびその他の情報はすべてノードとなります。
ノード中心の規則に従うことで、ツリーの一貫した表現方法が提供されます。残念ながら、DOM は他のモジュールに比べて Perl との類似性が低いので、多少、使いにくいかもしれません。一方、Perl で使用する方法や関数呼び出しは、Perl 以外の言語で DOM をプログラミングする場合でも同じように使用できます。
次のような簡単な XML 文書があるとしましょう。
<paragraph align="left">The <it>Italicized</it> portion.</paragraph> |
図 1 に示す、DOM によってツリーとして表現したサンプル XML を見てください。
図 1. DOM ツリーはサンプル XML 文書の各部分をノードとして処理する
図 1 で、ツリーのDocument、Element、Text、およびAttr の部分はXML::DOM::Node です。
DOM は、文書ツリーにアクセスし、操作するための機能セットを提供します。これらの機能は、W3C の DOM 推奨では、言語中立のインターフェースとして指定されており、DOM パッケージはこれらのインターフェースを実装しています。Perl の XML::DOM パッケージに、これらの機能とその使い方に関する詳しいドキュメンテーションが含まれています。
次に、DOM パーサーの簡単な呼び出しを示します。
use XML::DOM;
# Read and parse the document
my $parser = new XML::DOM::Parser;
my $doc = $parser->parsefile ("filename");
|
これにより、所定のファイルが解析され、DOM 文書がメモリー内に作成されます。doc 変数は、この文書のハンドルです。
XML 文書を処理する際にハンドラーのコールバックが生成されるイベント・ベースのモデルと異なり、ツリー・ベース・モデルでは、結果オブジェクトの明示的な走査が必要です。走査は、ルート・ノードから子ノードの順番に実行されます。ツリー走査の再帰的な実装は次のようになります。
sub traverse {
my($node)= @_;
if ($node->getNodeType == ELEMENT_NODE) {
print "<", $node->getNodeName, ">";
foreach my $child ($node->getChildNodes()) {
traverse($child);
}
print "</", $node->getNodeName, ">";
} elsif ($node->getNodeType() == TEXT_NODE) {
print $node->getData;
}
}
|
ノードがElement ノードの場合は、XML タグを表し、子ノードが含まれている可能性があります。それぞれの子ノードは、traverse 関数の再帰的呼び出しによって処理されます。Text ノードの場合は、テキスト・データが単に出力されます。
オブジェクト指向の Perl の使用に注意してください。DOM ツリーの各ノードは、一連のメソッドを持つオブジェクトです。たとえば、getNodeType メソッドは、ノードのタイプを戻します。XML::DOM は、オブジェクトを幅広く利用します。利用可能なすべてのメソッドについては、XML::DOM のドキュメンテーションを参照してください。
文書を走査するのではなく、文書内の特定のノードやサブツリーにアクセスしなければならない場合もあります。DOM には、文書内にランダムにアクセスしたり移動したりするためのメソッドが用意されています。
特定のタグを検索する場合は、getElementsByTagName 関数を使用します。
my @matching_tags = $start_node->getElementsByTagName("my_tag");
my $first_match = $matching_tags[0];
foreach my $match (@matching_tags) { do_something; }
|
getElementsByTagName 関数は、指定されたタグ名にマッチするノード・セットを戻します。マッチングの実行はとても簡単です。start_node のサブツリー内にある、指定されたタグ名とマッチするすべてのタグが選択されます。そのため、名前は同じだけれどもコンテキストが異なる 2 つのタグを区別することはできません (たとえば、x を検索すると、<a><x /></a> と<b><x /></b> の両方が選択されます)。ただし、この関数はXML::DOM::Node のメソッドとして利用できるため、開始ノードをインテリジェントに選ぶことにより、正しい選択を行うことができます。
また、親子の関係を使用することで、ツリー内を移動することもできます。たとえば、次は、現在のノードの親の最初の子を指定します。
my $other_node = $start_node->getParentNode->getFirstChild; |
XQL (XML 照会言語) は、XML コンテンツ上の高度な照会の標準です。XQL は、ここでは詳しく扱いません。XML::XQL のドキュメンテーションに、すばらしいチュートリアルが入っています。ここでは、比較的簡単な照会を実行してみましょう。
my @matches = $start_node->xql("stock_quotes/stock_quote/price"); |
この照会は、stock_quote 要素内で要素price のオカレンスを検索します。stock_quote そのものは、stock_quotes 要素の中にあります。照会には、条件を含めたり、属性で検索したりすることもできます。
my @ask_price_nodes = $stock_quote->xql("price[\@type='ask']"); |
この場合は、属性type の値がask である price ノードが検索されます。
メモリー内の DOM を変更して、ノードやサブツリーを追加/削除することができます。新しいノードを追加するには、新しいノードを作成し、その値を設定して、ツリー内の適切な場所に追加します。XML ファイル 内に、ノードpercentage をchange の子として追加するには、次のようにします。
my $newnode = $doc->createElement("percentage");
$newnode->addText("23");
my $change_node = $doc->getElementsByTagName("change")->item(0);
$change_node->appendChild($newnode);
|
doc は、通常はパーサーによって作成される、XML::DOM::Document オブジェクトです。新しい element ノードは、createElement メソッドを使用して作成します。テキスト、コメント、属性などの他のノード・タイプにも同じようなメソッドがあります。
新しい element ノードを作成し終えたら、次にその値を設定し、ツリー内で親の場所を探して、appendChild メソッドを使用してツリーに追加します。
次に、DOM を使用して、前回のXML とスクリプト言語 のサンプル・アプリケーションを作成し直します。このアプリケーションは、簡単な株取引プログラムです。XML フォーマットで株式相場を取得し、データベースから取引規則を取得して、規則と株式相場に基づいてどのような取引を実行するかを決定します。
最初のステップは XML ファイルの読み取りです。
my $parser = new XML::DOM::Parser; my $doc = $parser->parsefile ($file); |
次に、データベース接続をオープンします。
use DBI; my $dsn = "DBI:mysql:database=test;"; my $dbh = DBI->connect($dsn); |
メモリー内の XML データを DOM とし、データベース接続を確立したら、XML ファイル内の各stock_quote でループを実行します。
foreach my $stock_quote ($doc->getElementsByTagName("stock_quote")) { |
次に、このstock_quote が参照する株式記号を認識する必要があります。
my @symbol_nodes = $stock_quote->getElementsByTagName("symbol");
my $symbol_node = $symbol_nodes[0];
my $symbol = $symbol_node->getFirstChild->getData();
|
記号がわかれば、この株に適用する規則を検索できます。
my $sth = $dbh->prepare("select * from rules where symbol='$symbol'");
$sth->execute(); |
取引規則 は、指定されたフィールド (価格、数量、変化など) とそれらのフィールドの値に基づいて取るアクションとして格納されています。たとえば、
INSERT INTO rules VALUES ("MSFT", "volume", "65000000", "sell");
|
は、数量が 65000000 を超えた場合に MSFT を売却することを示します。詳細については、XML とスクリプト言語を参照してください。
各規則ごとに、XML 株価の適切なフィールドの値と、規則で指定されているしきい値を比較します。
while (my $ref = $sth->fetchrow_hashref()) {
my $field = $ref->{'field'}; my $value = $ref->{'value'}; my $action = $ref->{'action'};
my $matching_field = $stock_quote->getElementsByTagName($field)->item(0);
$value_from_xml = $matching_field->getFirstChild->getData();
if ($value_from_xml > $rule_threshold) {
take_action($symbol, $action);
}
}
|
これで、株取引アプリケーションの基本ロジックを実装しました。値は、タグのテキスト・コンテンツとしてではなく、属性として格納されるので、price タグの特殊なケースを追加する必要があります。
if ($field eq "price") {
my @ask_price_nodes = $stock_quote->xql("price[\@type='ask']");
my $ask_price_node = $ask_price_nodes[0];
$value_from_xml = $ask_price_node->getAttribute('value');
}
|
各株式相場には、それぞれ言い値、初値、その日の高値、低値を指定する、複数のprice 要素が割り当てられています。XQL を使用して、現在のstock_quote 内で、属性type がask に設定されているprice タグを探します。つまり、その株の言い値を探します。XQL の使用により、コードは著しく簡易化されることに注意してください。price ノード内をループして、それぞれのtype 属性を調べる必要はありません。
別リストになっている完全なプログラムは、XML Today Web サイト (参考文献 を参照) から入手可能なライブの XML 株式相場サーバーで生成することができる、XML フォーマット設定された株式相場を操作するプログラムです 。
一連の取引規則とXML フォーマット設定された株式相場でこのプログラムを実行すると、次の出力が生成されます。
pl domactive.pl stocks.xml ** Dealing with IBM: ************** Handling rule: if (price > 100) take action sell. Value for price from xml value = 109.1875 Rule "price > 100" applies for IBM Taking action "sell" on stock "IBM" . ** Dealing with MSFT: ************** Handling rule: if (volume > 65000000) take action buy. Value for volume from xml value = 64282200 Rule "volume > 65000000" does not apply for MSFT, no action taken. ** Dealing with CSCO: ************** Handling rule: if (change > 3) take action sell. Value for change from xml value = +2 Rule "change > 3" does not apply for CSCO, no action taken. ** Dealing with INTC: ************** ** Dealing with ORCL: ************** ** Dealing with SUNW: ************** |
ツリー・ベースで XML ファイルを処理する方法を使用すれば、プログラミングを簡易化し、データにランダムにアクセスすることができます (ストリーム・ベースの方法ではリニア・アクセスしか行えませんでした)。XQL などの強力な照会機能により、文書全体を明示的に走査することなく、必要な情報のセグメントを簡単に探し出すことができます。今日では、これらの強力なツール、上記の例、および Web 上のオンライン文書や印刷文書などを利用すれば、XML ベースのアプリケーションを速く、簡単に作成することができます。
- このシリーズの第 1 回 XML とスクリプト言語では、Perl およびスクリプト言語を使用した XML 処理のバックグラウンドについて説明し、イベント・ベースのモデルを紹介しました。
-
The Document Object Model (DOM) Level 1 Specification では、HTML 文書および XML 文書を表すためのオブジェクトの標準セット、およびそれらにアクセスおよび操作するための標準インターフェースを提供しています。
-
XML::Grove では、Perl ハッシュのツリーを使用して、解析後の XML、HTML、または SGML インスタンスの情報セットに簡単にアクセスすることができます。
- Groves とその概念については、Groves: an illustrated example を参照してください。
- Grove の内部作業と、Grove が重要な理由については、An Introduction to Groves を参照してください。
-
MySQL Database は、ほとんどの種類の UNIX、および Windows と OS/2 を含む、大半のオペレーティング・システムで利用できる無料の SQL データベースです。
- Perl、XML、およびデータベースを使用してハイ・パフォーマンスな Web アプリケーションを作成する場合の問題およびテクニックについては、High Performance Web Applications using Perl, XML, and Databases を参照してください。

Binary Evolution Inc. の社長であり共同設立者でもある Parand Tony Darugar 氏は、1992 以来、インターネット・ベースのベンチャー・ビジネスにかかわってきました。興味の対象としては、インターネット商取引、データベースやレガシー・システムからの Web 接続、XML、人工知能などがあります。連絡先はtdarugar@binev.com です。