実用的なXML: XPath 2.0を使い始める

新しいデータ・モデルを理解する

Comments

おなじみの標準の大幅なアップデート

XPath 2.0は、まだ勧告候補ですが、正式な承認に向けて動いています。1999年以来初めてのXPath勧告へのアップデートは、市場から熱望されており、当然ながら幾つかのツールが既に最新ドラフトの実装を始めています。この変更は非常に根本的なものであるため、やがて世界はXPath 1.0をXPath 2.0のドラフトとして見るようになるだろうと私は思っています。

XPath 2.0勧告は、XSLT 2.0とXQuery 1.0の基礎をなしています。どちらも、そのクエリー・エンジンのコアとしてXPathを使用しており、またそのコアを、結果をフォーマットするためのステートメントで補強しています(参考文献を見てください)。

XPath 1.0とXPath 2.0との間で大きく変更されたものは数多くありますが、その一部は次のようなものです。

  • ノード・セットではなく、シーケンスに基づく新しいデータ・モデル
  • 変数をバインドする機能(これまでは、変数はホスト言語(XSLT)にバインドされていました)
  • XML Schemaデータ型の完全サポート
  • 正規表現や日付/時間、ストリング操作など、数多くの新機能
  • コメント(これは主要機能ではありませんが、クエリーをデバッグする際に便利です。つまりテスト用に、パスの一部を注釈にできるのです)

この記事では、新しいデータ・モデルについて、もっと具体的に言うと、シーケンスの使い方を中心に説明します。これらは表現力という点から見て、最も根本的な変更なのです。

XPath 2.0でのシーケンス

XPath 2.0は、すべてをシーケンスとして処理します。『シーケンス』は、様々な異なるアイテムの集まりを順番に並べたものです。アイテムは、XML文書のノード、あるいはアトミックな値のいずれかです。アトミックな値は、XML Schema勧告で定義される型であれば、(複合型を含め)どんな型でも構いません。XPathでシーケンスを宣言するためには、単純にアイテムをカンマで区切り、シーケンス全体を括弧で囲みます(下記)。

(2, 'declencheur', 5.10)

現実問題として、有効なXPath 1.0リクエストであれば、ほとんどすべてXPath 2.0でも有効なままです。言い換えると、XPath 2.0は、おなじみのXPath 1.0構文を保持しています。つまり、パスは相変わらず、フォワード・スラッシュ( / )で区切られたロケーション・ステップから構成されるのです。例えば次のようなものです。

/po:PurchaseOrder/po:ProductList/po:Name

.

しかしXPath 2.0でのロケーション・ステップは、アイテム(繰り返しますが、これらのアイテムはXMLノードであるかも知れません)を、ツリー(XPathでの1.0データ・モデル)のノードではなく、シーケンスの中のアイテムとして認識します。

XPath 2.0におけるすべての概念は、シーケンスを基にするように変更されています。例えば、XPath 1.0でノード・セットを想定していたファンクションは、今度はシーケンスで動作します。

XML文書は階層的であることを考えると、XPath 1.0のモデル(ツリー構造)には意味があります。しかしXPathはツリーを生成できないため、ある1つのリクエストからの結果を別のリクエストに渡して処理を進める、ということができず、このモデルでは限界もあるのです。また、SQLスタイルの複雑なリクエストを書くことができません。

シーケンスを使う

先に触れたように、XPath 1.0の構文はそのまま使用できます。しかしXPath 2.0では、特にシーケンスを扱うための、新たなステートメントも幾つか導入されています。最初に、for式を見ることにしましょう。名前からも分かる通り、for式は、シーケンス中のアイテムに対してループ動作を行います。

典型的なfor式は、リスト1のようなものです。

リスト1. XPath 2.0の例
for $line in /po:PurchaseOrder/po:OrderLines/po:Line
return $line/po:Price * $line/po:Quantity

上記のXPathは、リスト2のような注文書(purchase order)に対して実行されます。そうすると、注文を表す各ラインの合計を計算し、次のようなシーケンスを返します。

(29.99, 89.98, 80, 3.1)
リスト2. 注文書(XML文書の例)
<?xml version="1.0" encoding="ISO-8859-1"?>
<po:PurchaseOrder xmlns:po="http://www.marchal.com/2006/po">
<po:Buyer>Pineapplesoft<po:Buyer>
<po:Seller>Bookstore<po:Seller>
<po:OrderLines>
<po:Line>
<po:Code type="ISBN">0-7897-2504-5<po:Code>
<po:Quantity>1<po:Quantity>
<po:Description>XML by Example<po:Description>
<po:Price>29.99<po:Price>
</po:Line>
<po:Line>
<po:Code type="ISBN">0-672-32054-1</po:Code>
<po:Quantity>2<po:Quantity>
<po:Description>Applied XML Solutions<po:Description>
<po:Price>44.99</po:Price>
</po:Line>
<po:Line>
<po:Code type="ISBN">2-10-005763-4<po:Code>
<po:Quantity>2<po:Quantity>
<po:Description>Huit Solutions Concrètes avec XML et Java</po:Description>
<po:Price>40.00<po:Price>
<po:Line>
<po:Line>
<po:Quantity>1<po:Quantity>
<po:Description>Internet Magazine<po:Description>
<po:Price>3.10<po:Price>
<po:Line>
</po:OrderLines>
<po:PurchaseOrder><

リスト1では、forはキーワードであり、各ラインから成るシーケンスに対してループ動作を行い、各アイテム(各ライン)を、$productという変数にバインドします。このパスでは、シーケンスを選択するために、XPath 1.0のようなロケーション・ステップ(po:PurchaseOrder/po:OrderLines/po:Line)を使用しています。

次は、for式の戻り部分です。この戻りによって、シーケンスが動的に作成されます。要は、これによって、ループ中のすべてのアイテムに対して、出力シーケンスにゼロ、1つ、あるいはそれ以上のアイテムが追加されるのです。

シーケンスを戻すことは非常に重要です。なぜなら、XPathによってシーケンスをさらに処理することができるからです。例えば、戻されたシーケンスをsum() ファンクションに渡せば、注文書の合計が簡単に計算できます。sum() はXPath 1.0のファンクションですが、シーケンスに対応するように拡張されています。これをリスト3に示します。

リスト3. XPathの結果を処理する
fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line
   return $line/po:Price * $line/po:Quantity)

今まではどうであったか

リスト4は、基本的にはリスト3と同じアルゴリズムですが、XPath 1.0とXSLT 1.0を使って実装されています。

リスト4. XPath 1.0を使って合計を計算する
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:po="http://www.marchal.com/2006/po"
xmlns:exslt="http://exslt.org/common"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="lines">
<xsl:for-each select="/po:PurchaseOrder/po:OrderLines/po:Line">
<line-total><xsl:value-of select="po:Price * po:Quantity"/><line-total>
<xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum(exslt:node-set($lines)/line-total)"/>
<xsl:template>
</xsl:stylesheet>

リスト4は、まず個々のラインの合計を計算し、その結果をsum() ファンクションに渡しています。しかしXPath 1.0では、変数はホスト言語(この場合はXSLT)で宣言する必要があります。そのため一時的な結果セットは、linesという変数の中に組み込まれます。次に、この変数の内容が2番目のXPathに与えられ、このXPathが注文書の合計を計算します。

リスト3リスト4を比較すると、XPath 2.0では大幅に表現力が向上していることが分かります。リスト4には、XPathステートメントが1つではなく2つあり、また、中間結果のコミュニケーションに関してホスト言語(XSLT)に依存しています。またリスト4はリスト3よりも読みにくく、リクエストを2つのXPathステートメントに分割しているため、クエリー最適化の可能性が制限されています。

条件式(conditional expression)

XPath 2.0では、条件式(if)も導入されています(リスト5)。この構文は自明でしょう。括弧の中の式が真、あるいは偽と判断されると、この式はthenあるいはelseセクションを返します。

リスト5. 条件式
if(/po:PurchaseOrder/po:Seller = 'Bookstore') then 'ok' else 'ko'

量化表現式(quantified expressions)

シーケンスに関する議論では、量化表現式(quantified expressions)に触れないわけには行きません。『量化表現式』というのは、要はシーケンス全体に適用されるテストなのです。量化表現式には、everyとsomeという2つがあります。

リスト6は、every式です。これは2つのセクションから構成されています。最初のセクションは、(ちょうどループと同じように)シーケンスに変数をバインドし、次に、シーケンス中のアイテムが満足すべき条件を規定します。量化表現式とループとの違いは、『条件式』がブール値を返すのに対して、『ループ』はシーケンスを返す点です。

具体的に言うと、every式は、シーケンス中の全アイテムについて条件が真の場合は真を返し、some式は、シーケンス中の少なくとも1つのアイテムについて条件式が真であれば真を返します。

リスト6. 量化表現式
every $line in /po:PurchaseOrder/po:OrderLines/po:Line satisfies $line/po:Code

リスト2の文書に対してリスト6を実行すると偽が返ります。これは、4ライン目にpo:Code要素がないためです。もしeveryキーワードをsomeで置き換えれば、少なくとも1ラインにはpo:Code要素があるため、この式は真を返します。

無限の組み合わせ

XPath 2.0の力は、式を組み合わせて高度なリクエストを作成できることから生まれています。リスト7は、注文書の合計を、別の公式を使って計算しています。つまり製品コードを含むラインのみがカウントされ、(製品コードを持たないものは恐らく出荷できないので)他のラインは黙って無視されるのです。条件が満足されない場合にemptyシーケンスを返すif式を追加するだけでよいので、このコーディングは単純です。

リスト7. 式を組み合わせる
fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line
return if($line/po:Code) then $line/po:Price * $line/po:Quantity else ())

以上をまとめると、XPath 2.0はシーケンスに基づく新しいデータ・モデルを採用したため、複雑なリクエストを非常に簡単に書けるようになっています。これまで、大量のXSLTコードを必要としたリクエストを、今やXPathのみで書けるようになったのです。


ダウンロード可能なリソース


関連トピック

  • XSLTはどのような言語か(developerWorks, 2001年2月、2005年4月に更新)では、著者のMichael Kayが、この言語の由来や、得意なこと、この言語を使うべき理由など、様々な側面からXSLTを解説しています。
  • Influences on the design of XQuery(developerWorks, 2003年9月)では、XQueryのパイオニアであるDon Chamberlinが、XQuery言語の出現やXMLデータに対するクエリー言語の必要性、その背景にある基本的な原則などについて解説しています。
  • XQueryの紹介(developerWorks, 2002年2月)を読んでください。著者のKevin Williamsが、XQueryの核心であるFLWR(「flower」)文の使い方を解説しています。
  • XSLT 2.0とXQueryの比較(developerWorks, 2006年4月)を読んでください。著者のBenoit Marchalが、XPath 2.0に対する2つのホスト言語を比較しています。
  • XQuery入門(developerWorks, 2001年6月、2006年1月に更新)では、Howard Katzが、XQueryの歴史的背景や文書化へのロードマップ、仕様の現状スナップショットなどを解説しています。
  • developerWorks XMLゾーンには、皆さんのXMLスキル向上に役立つ記事やチュートリアルが豊富に取り揃えられています。
  • XMLおよび関連技術においてIBM認証開発者になる方法については こちら を参照してください。
  • SaxonでXSLTとXQuery処理を結び付けてください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=242788
ArticleTitle=実用的なXML: XPath 2.0を使い始める
publish-date=05302006