目次


Java プラットフォームから XPath を評価する

Java コードを使って XML 文書の一部を選択する

Comments

XML 技術をグループ分けする

10 年ほど前、XML が最もホットな技術の 1 つとして人気の絶頂だった頃、その後 10 年の間に XML がこれほど重要になることは想像できませんでした。さらに現在では、XML と同様に興味深い、さまざまな XML 関連の技術が存在しています。実際、さまざまな XML 技術をいくつかの観点から見ることができます。

従来の XML グループ

XML 関連の技術は、次のようないくつかの基本グループ、つまりその技術が主に焦点を当てる領域ごとにグループ分けすることができます。

  1. 文書を作成するための技術: このグループは、実際に XML の作成に大部分の時間を費やす人達のためのグループです。オリジナルの XML データを作成するにせよ、あるいは既存のデータを XML フォーマットで表現するにせよ、このグループは純粋な XML のみに焦点を当て、そうした文書を使用する可能性のあるプログラミング作業には、ほとんど注意を払いません。このグループには XML の中核部分と特定の XML ボキャブラリー (MathML や科学に関するいくつかの XML ボキャブラリーなど) とがあります。
  2. XMLを処理するための技術: このグループは、あるフォーマットから別のフォーマットに XML を変換したり、加工したり、あるいはマイグレートしたりする XSL などの技術を指します。このグループの焦点も XML 文書と XML 文書の中のデータですが、そうした変換を行うためにプログラミング言語が使われることも時々あります。
  3. XML の読み取り/書き込み (そしてデータの永続化) を行うための技術: これは技術の中でもプログラミングを中心とした技術グループであり、焦点を当てる範囲は下位レベルの API (SAX やDOM など) からデータ・バインディング技術 (JAXB や Castor など) にまで広がっています。このグループでは XML はデータを保存するための手段の 1 つと見なされており、さまざまな方法における目的達成手段の 1 つと見なされています。

最近まで、大きなカテゴリーはこの 3 つであり、新しい技術や仕様の大部分は この 3 つのグループのうちのどれか 1 つに追加されてきました。

XML をファーストクラスのデータへと移行する

しかし XML に関する大きな問題の 1 つで、上記 3 つのグループにも存在している限界は、適切な検索がサポートされていないことでした。データを検索したい場合に、そのデータが XML に含まれていると、うまく検索することができませんでした。実際、一般的な解決方法としては、上記のグループ分けのいくつかを強制的に組み合わせて適用することでした。例えば文書を作成する人の場合であれば、苦労しながら grep などのコマンドライン・ツールを使うかもしれません。しかし、grep は検索を行うための方法としては原始的です。プログラマーであれば、(彼らにとっては別のグループである) XML を読み込んで、プログラミング言語 (Java や C# など) を使って、XML ではないフォーマットでデータを検索します。この方法でも使用には耐えますが、やはり XML の限界が明らかになります。

幸い、XPath (そして、この記事の後の方で説明する XQuery ) が導入され、今や一般的になったため、4 つ目の新たなグループが登場しました。

  1. XML を検索するための技術: XPath と XQuery はこのグループに入ります。これらの仕様と技術を利用することによって、XML に適した方法で XML 文書を検索することができます。つまり XML のセマンティクスを生かした検索をすることができ、しかも XML 文書の中のデータを検索できるだけではなく、そうした XML 文書の構造も検索することができます。

XPath と XQuery を利用すると、XML からデータを取り出してプログラミング言語の中に取り込み、そのプログラミング言語のツールを使ってデータを検索する、という面倒なことを行う必要がなくなります。そうした方法ではプログラミング言語による制約がある上、通常は XML のセマンティクスや構造 (どの要素が他のどの要素の子だったのか、など) の大部分が失われてしまいます。しかし、XPath と XQuery を使うことによって、プログラミング言語を使わなくても XML を検索することができるのです。

ただし、そうは言っても相変わらずプログラミング言語は必要であり、Java 言語 (あるいは他の言語) を使って XPath と XQuery とのやり取りを行う必要があります。XPath と XQuery を使えば XML に対応した素晴らしい検索機能を実現することはできますが、プログラマーであれば、やはりこれらの技術を利用する方法が必要になってきます。システムに対応したコマンド (exec() など) を使ってコマンドライン・プロセスを起動するだけの方法は面倒であり、とても対処することのできないあらゆる種類のエラーが発生することになりがちです。さらに悪いことに、その方法ではおそらく、検索の結果を処理することが (実際に不可能ではないにしても) ほとんど不可能に近くなります。そこで XPath を Java (あるいは C# または Perl) 言語で利用することになるのです。この記事は Java 言語のみを扱います。(他の言語に関する記事を読みたい方は、この記事のフィードバックのセクションからその旨を連絡してください。)

この記事を読むには、少なくとも XPath について理解している必要があります。今まで XPath を使ったことがない人は、XPath に関する 2 回シリーズの入門用チュートリアルへのリンクを「参考文献」に挙げましたので、それを読んでからこの記事を読んでください。

JAXP と Java 5

このポイント、つまり XPath と Java 技術が交わるポイントで、Sun は Java プログラマーに大きな便宜を図ってくれたのです。Sun は Java 5 の環境に XPath のサポートを統合しました。さらに良いことに、エンタープライズ版をダウンロードしたり、 (かつて Sun が JDBC の一部に関してよく行ったように) 追加パッケージをダウンロードしたりする必要がありません。マシンに Java 5 ソフトウェアがインストールされていれば、Java に非常に適した形で XPath がサポートされていることになります。実際、XPath のサポートは、おそらく既に皆さんにはおなじみの JAXP (Java API for XML Processing) の一部なのです。

Java 5 リリースがインストールされていることを確認する

どのバージョンの Java 技術がインストールされているのかわからない場合や、コードを作成する対象となるマシン (例えばリモートのサーバー) でどのバージョンの Java 技術が実行されるのかわからない場合には、-version フラグを付けて java コマンドを実行すれば、簡単にそれを知ることができます。リスト 1 は、このコマンドを実行した様子を示しています。

リスト 1. Java 5 以降のバージョンがインストールされていることを確認する
[bdm0509:~/Documents/developerworks/java_xpath] java -version
java version "1.5.0_13"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_13-b05-237)
Java HotSpot(TM) Client VM (build 1.5.0_13-119, mixed mode, sharing)

バージョンが 1.5 以降であれば、この記事の作業を行うための準備は完了です。1.5 は 5.0 と同じであることに注意してください。公開資料ではすべて「Java 5」と表現されているにもかかわらず、なぜ java コマンドで確認すると相変わらず 1.5 が返されるのか、(私や、ほとんどの Java 開発者には) よくわかりません。いずれにせよ 1.5.x さらには 1.6.x が返ってくれば、それで完璧です。それ以外の場合には「参考文献」に挙げたリンクを調べ、Java 5 技術をダウンロードしてください。

XPath のサポートがインストールされていることを確認する

次に、XPath のサポートがインストールされていることを確認します。これはおそらく余計な繰り返しに聞こえるかもしれません。このすぐ前に、少なくとも Java 5 ソフトウェアがインストールされていることを確認したからです。それでも、開発環境で使われているバージョンとは異なるバージョンの Java をシステム・パスにインストールしている開発者が大量にいるのです。あるいは皆さんの IDE である Eclipse は、Web アプリケーション・サーバー以外の何かを実行していることもあります。その他さまざまな要因があり、そうした知らず知らずのうちに生じている問題を回避するための最善の方法は、テストを行う簡単なプログラムを作成することです。リスト 2 は、XPathFactory という XPath ファクトリーの新しいインスタンスを作成することだけを行うプログラムを示しています。またこのプログラムによって、パーサーや実装が適切に設定されていることや実行されていることを確認することもできます。

リスト 2. XPath をテストするための非常に単純なクラス
import javax.xml.xpath.XPathFactory;

public class XPathTester {

  public static void main(String args[]) {
    try {
      XPathFactory factory = XPathFactory.newInstance();
    } catch (Exception e) {
      System.err.println("Uh oh...looks like you don't have the version " +
        "of JAXP with XPath support. Better upgrade to Java 5 or greater.");
    }
    System.out.println("Successfully loaded XPath factory. Things look good.");
  }
}

このクラスをコンパイルして実行します。すると非常に基本的な出力が得られるはずです (リスト 3)。

リスト 3. リスト 2 のテスト・クラスが成功した場合の出力
[bdm0509:~/Documents/developerworks/java_xpath] java XPathTester
Successfully loaded XPath factory. Things look good.

このクラスは非常に単純なものですが、皆さんが作成した XPath のコードが実行される可能性のある任意の場所 (Web サーバー上であったり、アプリケーション・サーバー上、あるいは本番サーバーをミラーリングした 4 台のサーバー上であったり等々) で、このクラスを実行することができます。これらのマシン上でこのクラスを実行できれば、もっと複雑な XPath アプリケーションを開発しても大丈夫です。もし、このテスト・クラスを実行できなかった場合には、(肝心なときに動作しないかもしれない) コードの作成に何時間もかける前に、まずは時間を取って XPath がサポートされるようにしてください。

XPath API の概要

JAXP API の XPath 部分を理解するためには、JAXP がどのように XML の構文解析や処理、変換のすべてを処理するかを十分に理解する必要があります。

AXP の基本的なワークフロー

XML を扱うためには次のような基本ステップが必要なことを思い出してください。

  1. ベンダー特有の JAXP 実装のインスタンスを提供するファクトリー・クラスを取得する。
  2. そのファクトリーから、パーサーまたはトランスフォーマーのインスタンスを取得する。
  3. そのパーサーまたはトランスフォーマーに対する構成オプション (妥当性検証や、名前空間を認識させるかどうか、使用するスタイルシートなど) を設定する。
  4. 操作の対象となる XML の保持や保存、参照のためのオブジェクトを (通常はいずれかのタイプの InputSource を利用して) 作成する。
  5. その XML を構文解析または変換する。

このステップは通常、リスト 4 のコードに似たものになります。リスト 4 は構文解析対象の XML 文書のファイル名をコマンドラインの引数で受け取って、単純に XML の構文解析を行っています。

リスト 4. SAXParserFactory を使う
import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

// SAX
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class TestSAXParsing {
    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println ("Usage: java TestSAXParsing [filename]");
                System.exit (1);
            }

            // Get SAX Parser Factory
            SAXParserFactory factory = SAXParserFactory.newInstance();

            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            SAXParser parser = factory.newSAXParser();
            parser.parse(new File(args[0]), new MyHandler());
        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support " +
                               " the requested features.");
        } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining SAX Parser Factory.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyHandler extends DefaultHandler {

    // SAX callback implementations from ContentHandler, ErrorHandler, etc.
}

DOM ツリーを作成する場合のプロセスも、やはり同じモデルに従います。リスト 5 は、ある XML 文書の DOM ツリーを作成するためのコードを示しており、これを見るとクラスやメソッドの名前が変わってもステップは非常に似ていることがわかります。

リスト 5. DocumentBuilderFactory を使う
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;

// JAXP
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

// DOM
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDOMParsing {

    public static void main(String[] args) {
        try {
            if (args.length != 1) {
                System.err.println ("Usage: java TestDOMParsing " +
                                    "[filename]");
                System.exit (1);
            }

            // Get Document Builder Factory
            DocumentBuilderFactory factory = 
                DocumentBuilderFactory.newInstance();

            // Turn on validation, and turn off namespaces
            factory.setValidating(true);
            factory.setNamespaceAware(false);

            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new File(args[0]));

            // Print the document from the DOM tree and
            //   feed it an initial indentation of nothing
            printNode(doc, "");

        } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not " +
                               "support the requested features.");

        } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining Document " +
                               "Builder Factory.");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void printNode(Node node, String indent)  {
        // print the DOM tree
    }
}

どちらの場合も、ファクトリーを入手し、そのファクトリーを使用して新しいパーサーやプロセッサーのインスタンスを作成し、そのインスタンスに対して操作を行います。

XPath のワークフロー

XPath のワークフローは、XPath のコードを作成する場合と非常に似ています。

  1. ベンダー特有の XPath 実装のインスタンスを提供するための、XPath のファクトリー・クラスを取得する。
  2. そのファクトリーから XPath エバリュエーターのインスタンスを取得する。
  3. 新しい XPath 式を作成する。(このステップは構文解析のモデルの場合とは異なりますが、それでも XML の変換モデルでスタイルシートを割り当てる操作に対応しています。)
  4. XPath 式を評価する対象となる、XML 文書の DOM ツリーを作成する。
  5. その XPath 式を評価する。

では、順を追ってこのステップを説明しながら XPath 式を解析するための基本プログラムを作成しましょう。そうすると、独自の XPath を評価することができ、あるいは XPath のチュートリアルを学んだ際に作成した XPath を評価することができます (このチュートリアルに関するリンクは「参考文献」にあります)。

作成した XPath に対して DOM ツリーを用意する

この記事で作成するプログラムにはいくつかの前提条件があることに注意してください。

  • 容易に DOM ツリーに変換できる XML 文書が用意されていること。この記事のサンプル・プログラムはコマンドラインから XML 文書を読み込んでそれを DOM ツリーに変換しますが、ネットワーク URI や一連の SAX イベント、その他のソースからも同じように容易に DOM ツリーを作成することができます。さまざまなソースから JAXP を使って DOM ツリーを作成する方法を忘れてしまった方は、そのために役立つリンクを「参考文献」に挙げてありますので参考にしてください。
  • 評価対象となる XPath が用意されていること。この記事では既に XPath があること、あるいは少なくとも XPath を作成する方法がわかっていることを前提にしています。XPath を作成する方法については説明しませんが、XPath を評価する方法については詳細に説明します。

これらの前提条件に対処できれば、コードを作成するための準備が整ったことになります。

作成した XPath を評価する対象となる XML 文書を用意する

まず、コマンドラインからファイル名を読み込む単純なプログラムから始めます。このファイル名を使って、そのファイル名によって参照される XML 文書から DOM ツリーを作成します。この場合には XPath 特有のものや、さらには JAXP 特有のものすらなく、単にいくつかの単純な I/O と、プログラムの接続作業があるだけです。このプログラムの先頭部分をリスト 6 に示します。これを XPathEvaluator.java として保存します。

リスト 6. XPath を評価するプログラムの最初のバージョン
package ibm.dw.xpath;

public class XPathEvaluator {

  public XPathEvaluator(String xmlFilename) {
    // Convert filename into a DOM tree
  }

  public void evaluateXPath(String xpathString) {
  }

  public static void main(String[] args) {
    try {
      if (args.length != 1) {
        System.err.println("Usage: java ibm.dw.xpath.XPathEvaluator " +
          "[XML filename]");
        System.exit(1);
      }
      XPathEvaluator evaluator = new XPathEvaluator(args[0]);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

XML を DOM ツリーに変換する

XPath API には (少なくとも JAXP での現在の形式には)、操作対象となる DOM ツリーが必要です。XPath は基本的に XML 文書の階層構造に関するものであるため、すべての XPath には操作対象となる何らかの形式のメモリー内モデルが必要です。DOM はそうしたモデルを、要素や属性、そしてテキスト・ノードから成る、ナビゲート可能なツリーの形式で提供します。

ここでは XPath をサポートするために既に JAXP を使用しているため、DOM サポートも無料で入手していることになります。DocumentBuilder クラス (そしてこのクラスに関連するファクトリーである DocumentBuilderFactory) を使用して、ある XML 文書に対するストリング参照をメモリー内の DOM ツリーに変換します。リスト 7 は、それを行うために XPathEvaluator に追加するものを示しています。

リスト 7. 入力 XML 文書から DOM ツリーを作成する
package ibm.dw.xpath;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class XPathEvaluator {

private Document domTree = null;

  public XPathEvaluator(String xmlFilename) {
try {
      // Convert filename into a DOM tree
      DocumentBuilderFactory domFactory =
        DocumentBuilderFactory.newInstance();
      DocumentBuilder builder = domFactory.newDocumentBuilder();
      this.domTree = builder.parse(xmlFilename);
    } catch (SAXException e) {
      throw new IOException("Error in document parsing: " + e.getMessage());
    } catch (ParserConfigurationException e) {
      throw new IOException("Error in configuring parser: " + e.getMessage());
    }
  }

  public void evaluateXPath(String xpathString) {
  }

  public static void main(String[] args) {
    try {
      if (args.length != 1) {
        System.err.println("Usage: java ibm.dw.xpath.XPathEvaluator " +
          "[XML filename]");
        System.exit(1);
      }
      XPathEvaluator evaluator = new XPathEvaluator(args[0]);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

このコードの大部分は単に DOM ベースの JAXP による構文解析にすぎません。ここで何が行われているのかわからない方は、JAXP の構文解析や変換に関する一般的な記事へのリンクを「参考文献」に挙げましたので、それを参照してください。

名前空間への対応に関する注意

XPath は XML に関する大部分の最新の仕様と同様で、名前空間に対応しています。つまり作成した XPath の一部として、要素に名前空間の接頭辞を使うことができます (例えば iTunes:artist など)。たとえ名前空間を持つ文書を使っていない場合であっても、将来のために必ずこの機能を持つ必要があります。

ただしそのためには、必ず DOM ツリーが名前空間に対応している必要があります。つまり作成した XPath を評価するための入力は名前空間に対応している必要があり、またその評価も名前空間に対応している必要があります。それを確実にするためには、DOM ツリーを作成する際に必ず名前空間への対応を有効にする必要があります。そのためにはリスト 8 に示す 1 行を追加します。

リスト 8. DOM ツリーの作成動作に名前空間への対応を追加する
package ibm.dw.xpath;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

public class XPathEvaluator {

  private Document domTree = null;

  public XPathEvaluator(String xmlFilename) {
    try {
      // Convert filename into a DOM tree
      DocumentBuilderFactory domFactory =
        DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
      DocumentBuilder builder = domFactory.newDocumentBuilder();
      this.domTree = builder.parse(xmlFilename);
    } catch (SAXException e) {
      throw new IOException("Error in document parsing: " + e.getMessage());
    } catch (ParserConfigurationException e) {
      throw new IOException("Error in configuring parser: " + e.getMessage());
    }
  }

  public void evaluateXPath(String xpathString) {
  }

  public static void main(String[] args) {
    try {
      if (args.length != 1) {
        System.err.println("Usage: java ibm.dw.xpath.XPathEvaluator " +
          "[XML filename]");
        System.exit(1);
      }
      XPathEvaluator evaluator = new XPathEvaluator(args[0]);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Java 環境で XPath を表現する

評価対象となる DOM ツリーが用意できたら、作成した XPath (単なるテキスト・ストリングにすぎません) を使ってその XPath の Java 表現を作成する必要があります。もちろん、これは String 変数を作成してその中に XPath を入れるという意味ではなく、実際の Java オブジェクトが必要になるという意味です。そしてその Java オブジェクトは、今用意した DOM ツリーに対して、Java オブジェクト自身を評価することが可能なオブジェクトであるか、もしくは別の XPath 対応のコンポーネントから評価されることが可能なオブジェクトでなければなりません。そこで、JAXP に新たに追加された API が登場します。

XPath ファクトリーから開始する

ここで、先ほどからのイベントのシーケンスが登場します。ここでは新しい javax.xml.xpath.XPathFactory クラスを使って XPath に関するすべての作業を開始します (ただし DOM ツリーを用意する作業は除きます。DOM ツリーの作成は、技術的に言えば実際に XPath を評価する前であればいつでも行うことができます。)

具体的には、XPathFactory はインターフェースであるため、まずこのインターフェースを実装する必要があります。この実装はベンダーごとに異なったものになることでしょう。つまり Sun はデフォルトの実装を提供しており、Apache は Apache 独自の実装をしている可能性があり、Oracle も独自に実装しているかもしれません。しかし、どのコードも、ベンダーに依存しない便利な形式のコードにはなっていません。そこで、XPathFactory と (XPathFactory の実装を作成する処理をしてくれる) XPathFactorynewInstance() メソッドを使って、ベンダー特有のコードを抽象化することができます。

リスト 9 はそのための処理を行います。このリストには evaluateXPath() メソッドしか示されていないことに注意してください。このメソッドを使うコードには、import 文 (すべて javax.xml.xpath パッケージの中にあります) をいくつか追加する必要があります。

リスト 9. XPathFactory のインスタンスを作成する
  public void evaluateXPath(String xpathString) {
XPathFactory factory = XPathFactory.newInstance();
  }

XPath オブジェクトを作成する

次に、XPath オブジェクトが必要です。このオブジェクトは XPath を評価することができ、作成した XPath に対応した Java プログラムの土台になるものでもあります。DocumentBuilderFactory から DocumentBuilder を作成するのと同じように、XPathFactory から XPath を作成することができます。リスト 10 はそのための最小限のコードを示しています。

リスト 10. XPath オブジェクトを作成する
  public void evaluateXPath(String xpathString) {
    XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
  }

このオブジェクトが用意できると、作成した XPath を評価し、その結果に対して作業をするための準備が整ったことになります。

XPath 式を評価する

XPath のインスタンスが用意できると、XPath を評価することができ、その評価結果としてのノード・セットを得ることができ、そしてそれらの結果に対して作業を行うことができます。

XPath を評価する

XPath (Java オブジェクトではなく、ある XML 文書を参照するストリング・パス) を評価するためには、XPath という Java オブジェクトに対する evaluate メソッドを使います。ここは少し混乱しがちな部分ですが、XPath という Java オブジェクトを使って XPath を評価します。つまり厳密な意味では XPath オブジェクトは XPath エバリュエーターなのです。

evaluate() メソッドは 2 つの引数を取ります。つまりストリング XPath (その XPath を評価する対象となる DOM ツリー) と、戻り型を示す XPath 定数の 2 つです。戻り型は非常に柔軟性に乏しいことがわかります。戻り型を指定しているのは実際には将来の互換性のためであり、ここでは結果が DOM の NodeList 構造として返されるように、必ず XPathConstants.NODESET を使います。

evaluateXPath メソッドに追加された、XPath を評価するためのコードを見てください (リスト 11)。

リスト 11. XPath を評価する
  publicNodeList evaluateXPath(String xpathString)throws IOException {
try {
      XPathFactory factory = XPathFactory.newInstance();
      XPath xpath = factory.newXPath();
return (NodeList)xpath.evaluate(
        xpathString, domTree, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      throw new IOException("Error evaluating XPath: " + e.getMessage());
    }
  }

リスト 11 には新たにさまざまな内容が追加されていますが、どれも次のように重要なものばかりです。

  1. このメソッドは今や org.w3c.dom.NodeList を返すようになりました。これを利用するためには、必ず import org.w3c.dom.NodeList; という文をコードに追加します。NodeList は作成した XPath を評価して得られたノードのリストを返すために使われる構造です。
  2. このコード・ブロック全体は try/catch ブロックの中にラップされており、XPath を評価した結果起こりうる例外 (javax.xml.xpath.XPathExpressionException) は IOException としてキャッチされ、再度スローされます。この処理を行う理由については、この後すぐに説明します。
  3. evaluate() を呼び出す際に渡されるのは、この evaluateXPath メソッドに渡される XPath ストリングと、このクラスのコンストラクターの中に作成される DOM ツリー、そして結果をノードのリストとして返すように指示する定数です。
  4. evaluate の結果は Object 型であるため、DOM の NodeList 型にキャストしてから返されます。

いくつかのことが行われていますが、どれも非常に単純であり、何もつまずくような要素はありません。

XPath 特有なのか、DOM 特有なのか、JAXP 特有なのか

1 つ興味深い点として、このメソッドから発生する例外やコンストラクターの中で発生する例外を、すべて IOException として返すことに決めています。これはクラスの設計上の決定事項であり、XPath に特有のものではありませんが、重要な決定です。このように決めることによって、(コマンドラインあるいは別のプログラムから) このクラスを利用する人は、すべての XPath のクラスやインターフェースについて、何も知る必要がなく、またこれらのクラスやインターフェースをインポートする必要も、直接使用する必要もなくなります。

実際ここでは、(NodeList クラスを除いて) すべての JAXP クラスや DOM クラス、SAX クラス、そして XPath クラスを抽象化して DOM から取り去りました。この方法では XPath を評価するために他のプログラマーが JAXP や XPath の API について理解している必要がないため、この方法は非常に強力です。この方法によってプログラムを興味深い演習問題から再利用可能なツールに高めることができますが、この違いは非常に重要です。

この原則をさらに一歩進めると、返される NodeList に対して評価結果を繰り返し処理して Java の List の中にダンプすることができます。こうすることによって DOM の詳細を完全に抽象化して見えなくすることができ、現在 org.w3c.dom.NodeList に依存しているという、わずかな依存関係さえもなくすことができます。

W評価の結果を処理する

XPath を評価した結果が得られたら、それらの結果に対して任意のフォーマットで作業を行うことができます。例えば、単純に評価結果を繰り返し出力することができます。もちろん、この動作はいくらでも自由に拡張することができます。

結果ノードに対する非常に単純な繰り返し

NodeList の各メンバーは実際には DOM の Node (org.w3c.dom.Node) であり、このノードの名前やタイプなど、このノードに関して必要なほとんどすべてを、ここから知ることができます。リスト 12 は XPathEvaluator クラスに対して非常に基本的な追加を行い、評価対象の XPath を渡し、結果を取得し、そして結果を出力するようにしたものを示しています。

リスト 12. XPathEvaluator プログラムを完成させる (その 1)
package ibm.dw.xpath;

import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XPathEvaluator {

  private Document domTree = null;

  public XPathEvaluator(String xmlFilename) throws IOException {
    try {
      // Convert filename into a DOM tree
      DocumentBuilderFactory domFactory =
        DocumentBuilderFactory.newInstance();
      domFactory.setNamespaceAware(true);
      DocumentBuilder builder = domFactory.newDocumentBuilder();
      this.domTree = builder.parse(xmlFilename);
    } catch (SAXException e) {
      throw new IOException("Error in document parsing: " + e.getMessage());
    } catch (ParserConfigurationException e) {
      throw new IOException("Error in configuring parser: " + e.getMessage());
    }
  }

  public NodeList evaluateXPath(String xpathString) throws IOException {
    try {
      XPathFactory factory = XPathFactory.newInstance();
      XPath xpath = factory.newXPath();
      return (NodeList)xpath.evaluate(
        xpathString, domTree, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      throw new IOException("Error evaluating XPath: " + e.getMessage());
    }
  }

  public static void main(String[] args) {
    try {
      if (args.length != 1) {
        System.err.println("Usage: java ibm.dw.xpath.XPathEvaluator " +
          "[XML filename]");
        System.exit(1);
      }
      XPathEvaluator evaluator = new XPathEvaluator(args[0]);
String xpathString = "//target[@name='init']/property[" +
                           "starts-with(@name, 'parser')]";
      NodeList results = evaluator.evaluateXPath(xpathString);
      for (int i=0; i<results.getLength(); i++) {
        Node node = results.item(i);
        System.out.print("Result: ");
        switch (node.getNodeType()) {
          case Node.ELEMENT_NODE: System.out.println("Element node named " +
                                    node.getNodeName());
                                  break;
          case Node.ATTRIBUTE_NODE: System.out.println(
                                     "Attribute node named " +
                                       node.getNodeName() + " with value '" +
                                       node.getNodeValue() + "'");
                                  break;
          case Node.TEXT_NODE:    System.out.println("Text: '" +
                                    node.getNodeValue() + "'");
                                  break;
          default: System.out.println(node);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

XPath に関する 2 回シリーズのチュートリアルを読んだ人であれば、この XPath によっていくつかのプロパティーが選択されることがわかるでしょう。このサンプル・プログラムを実行するためには (リスト 13)、xerces-build.xml というサンプル・ファイルをダウンロードする必要があります (まだ入手していない人は、「参考文献」のセクションで入手することができます)。

リスト 13. XPathEvaluator プログラムを実行する (その 1)
[bdm0509:~/java_xpath] java ibm.dw.xpath.XPathEvaluator xerces-build.xml 
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property
Result: Element node named property

これらの結果は、特に図 1 と比較した場合は、非常に面白味に欠けたものに見えます (図 1 はチュートリアルから引用したスクリーン・キャプチャーですが、チュートリアルでは XPath を評価するためのツールを使って同じ式を評価し、結果をグラフィカルに表現しています)。

図 1. グラフィカル・ツールを使って XPath 式を評価する
グラフィカル・ツールを使って XPath 式を評価する
グラフィカル・ツールを使って XPath 式を評価する

出力された要素のビューは一見単純そうですが実は複雑になっています。

ノードは名前以外のものを持つ

このサンプル・プログラムが行ったことは、ノードの名前とタイプ、そして場合によっては (そのタイプに応じて) ノードの値を出力することだけですが、それでも完全な Node オブジェクトが得られたことを忘れないでください。しかもこのノードは単独で存在しているのではなく、メモリー内にある DOM ツリーのノードへの参照になっています (ただし、この DOM ツリーは、XPathEvalutor コードの内部に隠れており、使おうと思っても参照できなくなっています)。

これが何を意味するかというと、各 Node に対して実際に得られたものは、XPathEvaluator に渡された完全な XML 文書の中での位置ポインターだということです。これはつまり、あるノードの子までナビゲートすることができ、ある要素ノードにどんな属性があるのかを調べることができ、あるテキスト・ノードの親要素の名前を知ることができ、その他 Node に対して許可されるどんな DOM 操作も行えるということです。つまり単にノードが得られただけではなく、そのノードに対する参照が、完全な DOM のコンテキストで得られたのです。このノードと、このノードが置かれたコンテキストを使って何をするかは、皆さんの判断次第です。

JAXP や DOM、XPath に関するこれまでの抽象化について

これまでの説明では DOM 特有の参照を避けるためにさまざまな工夫をしましたが、今度はまったくそうした工夫をしていないことに気付いた人がいるかもしれません。実際、XPathEvaluator によってクラスのユーザーから XPath の詳細が見えないように抽象化される一方、相変わらず DOM の NodeList が返される理由は、そこにあります。ユーザーが JAXP や XPath を使わなくても問題ないようにすることはできますが、XPath を評価した結果を使ってさまざまな作業を行うためには、やはり DOM に対して作業を行う必要があるのです。

そうした理由から、DOM の構造を返すようにし、ただし XPath 特有の入力を要求したり XPath 特有の出力を提供したりしないのがベストです。少なくともクラスが機能するために何が必要かということから考えると、DOM を扱う作業はユーザーに任せ、それ以外は任せないことです。

そして次は XQuery に・・・

皆さんや私のような開発者は短気であり、多くのことを心配します。Java 環境から XPath を使う感覚に慣れ、XPath を自由に使えるようになる頃には既に、XPath ではできないことは何かについて考え始めていることでしょう。データ間における特に複雑な関係は扱いが容易ではなく (SQL 風の結合を使う作業は XPath を作成する目的とは対極の作業です)、Java 環境でノードを順序付けした上でフィルタリングする必要があり、また XPath を使ったプログラムを理解するのは XPath の仕様をよく理解していない人には非常に困難です。

幸いなことに、皆さんは XPath から出発して、XPath の制約のすべてに (既に皆さんが行ったのとよく似た方法で) 対処する次のステップへと、とても自然なステップを進むことができます。XQuery は XML 化されたバージョンの SQL にさらに多くのものを追加しており、クエリーの作成や結果のソートと順序付けを行うことができ、またクエリーの中で実際に WHERE 文を使うことができます。また XQuery は XPath の上に構築されているため、これまでノードや、述部の突き合わせ、要素と属性との相互関連などに関して学んだすべての内容を XQuery にも応用することができます。

また XPath の場合とまったく同じように、XQuery には Java プログラマーが利用できる API である、XQJ (XQuery for Java) API があります。XQuery に関する詳細は、XQuery や XQJ に関する記事やチュートリアルへのリンクを「参考文献」に挙げましたので、そちらを参照してください。そして XPath を十分に理解できたら XQuery を検討し、XML 関連のアプリケーション・コードを一層強力なものにしてください。

まとめ

Java 技術で XPath を使うために必要なことの大部分は、単純に新しい構文を学び、API やいくつかのツールを構成し、そして XPath に関して既に知っていることを適用することです。ただし、だからといって Java 環境で XPath を使うことが簡単だと考えるべきではありません。XPath では複雑な処理が要求されますが、それ以上に XPath は Java プログラミングで XML を扱う際に驚くほどの柔軟性を発揮します。XPath によって、最も基本的な SAX や DOM、JAXP、JDOM などの実装によって提供されるものをはるかに超えるものを実現することができます (ただし一部のベンダーやプロジェクトは、これらの仕様や API が提供する基本機能に対して、XPath を扱える拡張機能を提供しています)。

そして XPath は、より複雑な XQuery 言語や、(XQJ API を使った) Java と XQuery の組み合わせに対する素晴らしい入り口となります。いきなり XQuery に移行するよりも、まず XPath に関するスキルを磨き、複雑なノード・セットを Java アプリケーションの中から選択する方法を学び、それらを必要に応じて操作する方法を学ぶ方が得策です。多くのケースでは、XPath 以上のものは何も必要ないことがわかるはずです。また XQuery は語彙の面でも XQJ API に関しても XPath の上に構築されています (XQJ API によって実際に XPath を評価することができ、また XQuery を実行することもできます)。つまり XPath を学ぶことで、自然と XQuery のスキルも磨かれてきます。そして何よりも、特に Java 環境から評価する場合には、XPath によって柔軟性が高まるのです。


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


関連トピック

  • XPath を十分に理解していない方は、この 2 回シリーズのチュートリアルをご利用ください。
    • Locate specific sections of your XML documents with XPath, Part 1」(Brett McLaughlin, Sr. 著、developerWorks、2008年6月) では XPath の基本を学びます。文書中の特定のデータを容易に見つけて参照する方法や、XPath のさまざまなセレクターやセマンティクスについて、サンプルを中心とした実践演習をとおして学びます。
    • Locate specific sections of your XML documents with XPath, Part 2」(Brett McLaughlin, Sr. 著、developerWorks、2008年6月) では、XPath のスキルをさらに磨くために、属性の値を評価しながら正確にノードを見つける方法や、ターゲット要素の親ノードと子ノードを見つける方法を学びます。
  • XPath 1.0 のサイトを訪れ、XPath の正式な定義をオリジナルの仕様で読んでください。
  • XPath 2.0 のサイトを訪れ、最新バージョンの XPath の仕様をオンラインで読んでください。
  • W3C が提供する簡単なチュートリアル、Tutorial on XPath を利用して、XML をより高度に使いこなすために XPath がいかに基本として重要かを理解してください。
  • 述部と関数がどのように動作するかを理解できたら、素晴らしいオンライン・リソースである XPath, XQuery, and XSLT Functions を訪れ、あまり一般的には議論されない構文や関数について調べてください。
  • Sun による XQuery for API のページを十分に理解したら XPath から XQuery に移行してください。このページでは Java API のための XQuery が完全に詳細に説明されています。
  • xquery.com のホストである DataDirect のリソース・ページでは、XPath エバリュエーター (Stylus Studio) と XQuery for Java (XQJ) エンジンの両方の実装、そして XQuery と XPath に関する関連の成果を利用することができます。
  • 索引付けされ、検索可能なこのリソース、DataDirect のオンライン・ヘルプ・システムを訪れてください。これは特定の DataDirect オブジェクトやメソッドに関して知るための素晴らしいサイトです。
  • XML および関連技術において IBM 認定技術者になる方法については、IBM XML certification を参照してください。
  • 皆さんのシステムに XPath のサポートを統合するために Java 5 SE をダウンロードしてください。
  • Java 6 ソフトウェア: Java 5 技術にアップグレードすることを検討中の場合には、可能であれば単純にバージョン 5 をスキップし、最新のバージョンに直接進んでください。
  • Stylus Studio 2008 XML をダウンロードし、Windows プラットフォームで XPath と XML 文書に関する作業を開始してください。
  • AquaPath をダウンロードし、Mac OS X で XPath による容易な位置評価を行ってください。
  • DataDirect's による Java 実装用の XQuery をダウンロードし、XQuery と Java による検索を開始してください。
  • Java & XML, Third Edition』(Brett McLaughlin と Justin Edelson の共著、2006年 O'Reilly Media 刊) は、XML や XSL、また関連する XML 仕様のいくつかを含めて、XML を最初から最後まで解説しています。

コメント

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Java technology
ArticleID=329650
ArticleTitle=Java プラットフォームから XPath を評価する
publish-date=07082008