XInclude と XQuery は Web プログラミングでのデータ処理を動的に行うための XML ツールです。XInclude を使用すると、複数の XML ファイルを 1 つのファイルのように扱うことができ、また XQuery を使用すると、さまざまなデータを組み合わせて処理し、Web ページの表示に組み入れることができます。XInclude と XQuery を併せて使用することで、こういったサービスをスマートかつ効率的に、ほんの数行のコードで行うことができます。
ほとんどのブラウザーは、直接、または XSL テンプレートと組み合わせることで、XML ファイルを表示したり処理したりすることができます。XInclude と XQuery もブラウザーで直接扱えると理想的ですが、現状のほとんどのブラウザーは、ユーザーに無理な要求をすることで XInclude と XQuery をサポートしているにすぎません (例えば実験的なアドオンをロードするようにユーザーに要求する、など)。多種多様なソースからデータを取得して組み合わせ、それらを 1 つの大きなデータセットとして処理するという作業は、Web プログラマーにとって大きな負担です。
この記事ではまず、架空のビジネスの例を通じて、XInclude と XQuery の組み合わせがどれほど強力かを説明します。次に、XInclude と XQuery による機能を PHP を使ってシミュレートする方法を学びます。すべてのデータ処理をサーバー・サイドに移すことで、XInclude と XQuery がブラウザーで限られたサポートしかされていないという問題に対処することができます。PHP を使うことによるもう 1 つのメリットとして、最終的な出力の表現方法を詳細に制御することができます。
3 つのガーデン・センターがある町を考えてみてください。3 つの店は互いに競争していますが、各店は大きく異なるサービスを提供しているため、敵対しながらと言うよりも、むしろ協力しながら競争しています。3 つの店は市場調査を行い、販売されている植物を顧客がどのように購入しているかを調べました。
園芸ビジネスでは、非常に傷みやすい商品の販売が短期間に大量に発生し、また顧客は求める商品の種類と質を非常に気にします。このビジネスにとって最大の関心事は、維持に手間がかかる商品 (例えば、水やりが必要で害虫がつかないように注意が必要な商品) を可能な限り早く販売し、適切な商品を適切なタイミングで、そして適切な場で提供することで、気が変わりやすい顧客に気に入ってもらうことです。そうしないと、顧客はそのようにして商品を提供できる店に行ってしまいます。
調査の結果、顧客は、求める植物がどこで買えるかを知るために何店にも電話するのを好まないことがわかりました。またビジネスにとっても、大量の電話を受けるのは不都合です。季節の初めには、どの店にも十分な商品があります。季節の終わりの方になると、事前の適切な計画にもかかわらず、商品によって欠品が出始めます。ペチュニアを求めている顧客は、3 つのガーデン・センターすべてに電話するか、あるいは 3 つのガーデン・センターすべてを訪れないと、求めているホットピンクの色を見つけられないかもしれません。一般に顧客は、どのガーデン・センターがどの植物を、どんなサイズのポットで、どれだけの量、どんな値段で売っているかを知りたがります。
3 人の IT マネージャーがミーティングを開きました。彼らは、どのガーデン・センターに特定の植物の在庫があるかを顧客が知ることができる、共通の Web サイトを作成することを決定しました。
3 つのガーデン・センターはすべてコンピューター化されていますが、それぞれのガーデン・センターは異なるシステムを使って情報を保存しています。Apples and Things という店では Microsoft® Access® データベース・システムを使用し、Birch Trees Unlimited という店では Linux® システムと MySQL を使用し、Carnation Tarnation という店では Mac OS® X と IBM® DB2 を使っています。
大きな方向性として、彼らは XML ベースのアーキテクチャーで Web サイトを作成することを決定しました。彼らにとって XML が便利なデータ・フォーマットである理由は、各ガーデン・センターのシステムが現在のデータを XML ファイルにエクスポートすることができ、その XML ファイルをクラウドの中で利用できるからです。マスター XML ファイルによって個々の店のデータを中央の 1 ヶ所に集めますが、この際に XInclude を使用します。最後に、メインの Web ページでそのマスター・ファイルを検証し、XQuery を使用してデータを抽出し、最終的な出力を表示します。
XInclude と XQuery を使って XML を処理する
各店はリスト 1 のような XML ファイルを作成することが、IT マネージャー達によって決められました。
リスト 1. 各店の XML ファイル
<store>
<info>
<name>...</name>
<address>... </address>
<phone>... </phone>
</info>
<plants>
<plant>
<name>Petunia</name>
<description>Pink, in 4 inch pots</description>
<quantity>100</quantity>
<price>3.00</price>
</plant>
<plant>
<name>Apple tree 'Spartan'</name>
<description></description>
<quantity>6</quantity>
<price>25.00</price>
</plant>
...
</plants>
</store>
|
このファイルには、store というルート要素と 2 つの子要素があります。info という子要素には、その店に関する一般的な情報が含まれています。plants という子要素には plant という子要素が数多く含まれ、それぞれの plant 子要素には、その植物に関する詳細な情報 (その植物の名前、説明、数量、価格など) が含まれています。3 つの店はすべて、正確にこれと同じパターンを使用します。
リスト 2 は、このシステムのためのメインの XML ファイルです。このファイルには 3 つの店のファイルがすべて組み合わされ、さまざまな商品の詳細が保持されています。そのため、このファイルは 1 つの大きなファイルとして見えます。
リスト 2. メインの XML ファイル
<?xml version="1.0"> <storeData xmlns:xi="http://www.w3.org/2001/XInclude"> <xi:include href="http://path-to-apples.xml"/> <xi:include href="http://path-to-birches.xml"/> <xi:include href="http://path-to-carnations.xml"/> </storeData> |
リスト 2 では、XInclude と HTTP を使用することで、クラウドの中にある 3 つの場所からデータを取得しています。この XML ファイルを処理すると、リスト 3 のようになります。
リスト 3. メインの XML ファイルを展開したもの
<?xml version="1.0">
<storeData ...">
<store>
<info>
<name>Apples</name>
...
</info>
<plants>
...
</plants>
</store>
<store>
<info>
<name>Birches</name>
...
</info>
<plants>
...
</plants>
</store>
...
</storeData>
|
リスト 3 では、リスト 2 の include 命令が個々の店の XML ファイルの内容で置き換えられています。各 store 要素は今や、グローバルな storeData 要素の子要素になっています。
今度は、このようにして組み合わされたデータセットに XQuery を適用します (リスト 4)。
リスト 4. XQuery を適用する
<div>{
for $store in doc("mainXMLfile.xml")/storeData/store
let $plants := $store/plants/plant
return
<div>{
for $plant in $plants
order by $plant/name
return <div>{ concat($plant/quantity," of ",$plant/name," at ",
$store/info/name," for ",$plant/price," each") }</div>
}</div>
}</div>
|
リスト 4 は、div セクションによって XHTML 出力を要求しています。各店に対して繰り返し処理を実行することで、各植物の数量と名前、その植物の在庫がある店の名前、植物の価格が出力されます。この出力はリスト 5 のようになります。
リスト 5. 生成された XHTML
<div>
<div>
<div>6 of Apple tree 'Spartan' at apples for 25.00 each</div>
<div>100 of Petunia at apples for 3.00 each</div>
</div>
<div>
<div>100 of Coleus at birches for 3.00 each</div>
<div>6 of Pear tree 'Flemish Beauty' at birches for 25.00 each</div>
</div>
<div>
<div>6 of Orchids at carnation tarnation for 25.00 each</div>
<div>100 of Roses at carnation tarnation for 3.00 each</div>
</div>
</div>
|
この出力のチェックは、多数ある XQuery クライアントのいずれかを使用して行うことができます。例えば、オープンソースで Java™ ベースの eXist アプリケーションを使用すると、テスト・データベースに対してさまざまな XQuery コマンドを実行した場合 (例えば、リスト 4 のクエリーを実行した場合) の出力を検証することができます。あるいは、スタンドアロンの XQuery アプリケーションである、Zorba を使っても同じことが可能です。これらの XQuery クライアントに関する詳細は「参考文献」を参照してください。この他に JavaScript を使う方法もあります。
XInclude と XQuery を組み合わせて使用する方法に代わる方法として、すべてのステップをサーバー・サイドで行う方法があります。それを PHP で行うためには、PHP の SimpleXML 拡張機能が提供する関数を使用します。SimpleXML により、フィルタリングされてフォーマット設定されたデータを直接ブラウザーに XHTML 形式で送信することができるため、サイト訪問者の負担を軽減することができます。
最初にインクルード・ステップについて説明します。インクルード・ステップは以下のように単純に文字列を置き換えるだけの作業です。
- まず、基本となる空の XML フレームワークを用意します。
- 各店に対する文字列を挿入します。
- 各店のファイルを取得します。
- そのファイルで店の名前を置き換えます。
以上によって、すべての店を含む 1 つの大きなファイルが作成されます。このファイルを別の方法で作成することもできます (例えば SimpleXML の addChild(...) 関数を使う方法など)。しかしこの場合は文字列置換の方が簡単であり、必要に応じていくらでも妥当性検証を行うことができます。リスト 6 は、この文字列置換の手法を示しています。
リスト 6. 文字列置換によってインクルードする
<?php
// string substitution approach
$arr = array("apples","birches","carnations");
$myxml = "<?xml version=\"1.0\"?>
<storeData>";
foreach ($arr as $a) { $myxml .= "$a\n"; }
$myxml .= "</storeData>";
// now process includes
foreach ($arr as $a) {
if (!$f = file_get_contents($a.".xml")) {
xml_fallback($a);
} else {
if (!simplexml_load_string($f)) {
xml_fallback($a);
} else {
xml_include($a,$f);
}
}
}
if (!$xml = simplexml_load_string($myxml)) {
echo "Combined file failed!\n";
} else {
echo $xml->asXML;
}
//
// functions
//
function xml_include($a,$rep) {
global $myxml;
$myxml = str_replace($a,$rep,$myxml);
}
function xml_fallback($a) {
global $myxml;
$rep = "<store><info>
<name>$a</name>
</info></store>";
$myxml = str_replace($a,$rep,$myxml);
}
?>
|
リスト 6 は最初に、店の短縮名を含む配列を宣言し、これらの名前を基本となる XML ファイルの中に組み込んでいます。次に、各店の XML ファイルに繰り返し処理を行い、XML ファイルを単純なテキストとして取得します。XML ファイルがない場合もあり、その場合には特別なアクションを実行する必要があります。XML ファイルがある場合には、その XML ファイル自体が妥当な XML であるかどうかを検証します。妥当な XML ではない場合には (XInclude の Fallback 機能に相当する機能を使用して) XML ファイルがない場合と同じアクションを実行します。その場合には、xml_fallback() 関数によって基本となる XML スニペットを置き換え、XML ファイルを整形式に保ちます。妥当な XML ファイルの場合には、その XML ファイルは xml_include() によってメイン・ファイルに挿入されます。最後に、完全に組み立てられた XML ファイルが整形式であるかどうかをテストします。この例ではインクルード機能をテストしているだけなので、整形式テストが成功すると処理が停止し、最終的な XML ファイルが出力されて人間の目による検証が行われます。ここまで来ると、この XML に対してクエリーを実行することができます。
この段階で、Zorba などの XQuery アプリケーションによって、あるいは SimpleXML を使って、PHP コードの中で XML を処理することができます。
Zorba を PHP に結びつけるための興味深い方法として、PHP Extension Community Library 拡張機能があります。(この方法の説明として、「参考文献」には developerWorks の記事、「XQuery を活用したアプリケーションを PHP と Zorba を使って作成する」へのリンクを挙げてあります)。あるいは、拡張機能を使わずに exec() を使用することで、PHP から Zorba を呼び出すこともできます。その PHP コード・スニペットを示したのがリスト 7 です。
リスト 7. 拡張機能を使わずに PHP と Zorba を使う
<?php
...
function run_zorba($myxquery) {
...
exec("zorba -i -q '$myxquery'",$array);
...
$xmlstr = implode("\n",$array);
$xml = simplexml_load_string($xmlstr);
echo $xml->asXML();
}
?>
|
リスト 7 では、run_zorba() 関数はまず初めに $myxquery ストリング引数の中に XQuery リクエストがあることを認識し、それから exec() 関数を使って Zorba を実行します。これをプロセスのフォークの中にラップした方が安全かもしれません。exec() 関数によって、$array 配列に Zorba の出力が書き込まれます。-q オプションと -i オプションはそれぞれ、クエリーの実行と出力のインデントを要求しています。-i オプションによって XML コードが読みやすくなるため、このオプションは特にデバッグの際に便利です。次に、配列の内容は 1 つの長いストリングに展開され、そのストリングが XML オブジェクトにロードされて (このテスト・ケースの場合には) エコー出力されます。
Zorba のような XQuery エージェントを使う方法は便利ですが、SimpleXML を使って直接データを抽出することもできます。この方法を以下に説明します。
この時点で、リスト 6 の $xml 変数が XML オブジェクトの中にロードされており、この XML オブジェクトを処理できる状態にあるとします。リスト 8 は、この XML データに対するクエリーの最終的なコードを示しています。
リスト 8. SimpleXML を使ってクエリーの処理を行う
<?php
// Query with PHP
include 'mainstore2.php';
if (!$xml = simplexml_load_string($myxml)) {
die("Cannot read output from XInclude step\n");
}
// begin output
$myout = "\n\n<html><head></head><body>\n";
$myout .= "<div>\n";
foreach ($xml->store as $store) {
foreach ($store->plants->plant as $plant) {
$myout .= $plant->quantity." of "
.$plant->name." at "
.$store->info->name." for "
.$plant->price
."\n";
}
}
$myout .= "</div>\n";
$myout .= "</body></html>\n";
echo $myout;
?>
|
リスト 8 はまず、リスト 6 と等価なものをロードし、この場合も XML が整形式かどうかをチェックしています。次に各店に対するループ処理、そして各店に在庫としてある植物に対するループ処理を行い、最終的にリスト 9 の出力を生成します。この出力は、先ほどの XQuery による方法で得られたリスト 5 の出力と等価です。この出力には、必要に応じて XHTML マークアップを追加することができます。
リスト 9. ブラウザーへの出力
<html><head></head><body> <div> 100 of Petunia at apples for 3.00 6 of Apple tree 'Spartan' at apples for 25.00 100 of Coleus at birches for 3.00 6 of Pear tree 'Flemish Beauty' at birches for 25.00 100 of Roses at carnation tarnation for 3.00 6 of Orchids at carnation tarnation for 25.00 </div> </body></html> |
この種のプログラミングは簡単に拡張することができます。例えば、ガーデン・センターの顧客が必要に応じてクエリー・パラメーター (価格の範囲や名前など) に基づいてフィルタリングやソートを行えるようにするのも簡単です。
手軽に使用できる SimpleXML ライブラリーを使って PHP でコーディングする方法と、XInclude と XQuery という特殊なライブラリーによって提供される中間的なツールと PHP を使う方法との比較は、プログラマーにとって関心のある内容です。特殊なツールから成るライブラリーに価値があるのは、プログラマーの負担を軽減でき、目的のジョブを行う上で必要なコードを削減および明確化できる場合のみです。
インクルードを行う場合、他のファイルのデータを挿入するのは簡単であり、SimpleXML ライブラリーを使う方法でも XInclude を使う方法でも、コーディング量はあまり大したことはありません。
クエリーを行う場合には、PHP+SimpleXML による方法には必要となる大量のコード (for ループなど) を XQuery ライブラリーによって置き換えることができます。
ただし、データを取得するためのコードが凝縮されていればいるほど、PHP が持つ他の機能を利用できる可能性は少なくなります。例えば、PHP+SimpleXML による 20 行のクエリーを XQuery による 1 行のクエリーで置き換え、コードを減らすことができます。ただしその代償として、20 個のステートメントの間に他のステートメントを簡単かつ明確に挿入することはできなくなります。長所と短所を考えて方法を選択する必要があります。
学ぶために
- W3C のサイトで XML Inclusions (XInclude) について調べてください。
- W3C のサイトで XML Query (XQuery) について学んでください。
- 「Use XQuery from a Java environment」(Brett D. McLaughlin, Sr. 著、developerWorks、2008年4月) を読み、Java アプリケーションから XQuery を使って文書を検索する方法を学んでください。
- 「PHP での SimpleXML 処理」(Elliotte Rusty Harold 著、developerWorks、2006年10月) を読み、PHP の SimpleXML 拡張機能について学んでください。SimpleXML を使うことで、PHP で扱いやすい構文を使って PHP ページで XML を照会、検索、修正、再公開することができます。
- 「XQuery を活用したアプリケーションを PHP と Zorba を使って作成する」(Vikram Vaswani 著、 developerWorks、2009年11月) を読み、Zorba を使って PHP に XQuery のサポートを追加する方法を学んでください。
- 「XQuery in the browser」(Ghislain Fourny らの共著、2009年4月) を読み、クライアント・サイドの Web プログラミングでの XQuery の可能性について学んでください。
- developerWorks の XML ゾーンには XML の領域でのスキルを磨くためのリソースが豊富に用意されています。
- My developerWorks で developerWorks のエクスペリエンスをパーソナライズしてください。
- XML および関連技術において IBM 認定技術者になる方法については、IBM XML certification を参照してください。
- developerWorks の XML ゾーンを XML の技術ライブラリーとして利用してください。広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM Redbooks などが用意されています。また、XML に関する他のヒント記事も読んでください。
- developerWorks の Technical events and webcasts で最新情報を入手してください。
- 今すぐ Twitter に参加して developerWorks のツイートをフォローしてください。
- developerWorks podcasts では、ソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
- developerWorks On demand demos をご覧ください。初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。
製品や技術を入手するために
- Zorba をダウンロードし、Zorba について学んでください。
- オープンソースのネイティブ XML データベース、eXist-db について調べてください。
- XQIB - XQuery in the Browser のサイトから XQIB をダウンロードしてください。XQIB を使うと HTML の中で XQuery コードを実行することができます。
- IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。
議論するために
- XML zone discussion forums に参加してください。これらのフォーラムでは XML を中心に議論が行われています。
- developerWorks blogs から developerWorks のコミュニティーに加わってください。
Colin Beckingham はカナダのオンタリオ州東部に住むフリーランスの研究者であり、ライターであり、プログラマーでもあります。Queen's University, Kingston で学位を取得している彼は、園芸、競馬、教育、行政サービス、小売業、旅行/観光業などにも関わってきました。彼はデータベース・アプリケーションの作成者であり、無数の新聞記事や雑誌記事、オンライン記事を執筆しており、また Linux でのオープンソース・プログラミングや VoIP、音声制御アプリケーションも研究しています。彼の連絡先は colbec@start.ca です。