W3C DOM 3 XPath の勧告に従った方法で XPath を評価する

Java を使用し、DOM 3 Load and Save Specification に従った方法で XML 文書を読み込み DOM を構築する方法は 昨日の記事 で書きました。

同じように DOM の勧告として DOM 3 XPath Specification があります。 この勧告は DOM ツリーを XPath を使って探索する際の API について記述しています。 今日はこの勧告に従った方法で XPath を評価するサンプルを書いておきます。

サンプル

Java SE 6 で実行し、動作することを確認しました。

package info.vividcode;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSParser;
import org.w3c.dom.xpath.XPathEvaluator;
import org.w3c.dom.xpath.XPathExpression;
import org.w3c.dom.xpath.XPathResult;

public class DOMXPathTest {
public static void main( String[] args ) {
try {

// ===== Load and Save に関する実装取得 =====
// DOMImplementationRegistry オブジェクトを取得 (ここから DOM 実装を取得できる)
DOMImplementationRegistry dir = DOMImplementationRegistry.newInstance();
// Load and Save 機能および XPath 機能を実装しているか,
// それらの機能と協調できる DOM 実装を取得
DOMImplementation imp = dir.getDOMImplementation("+LS 3.0 +XPath 3.0");
// 本来はここで imp が null でないかどうかチェックすべき
// (該当する DOM 実装が無ければ null になる)

// ===== XML 文書のパース =====
// Load and Save 機能を持つオブジェクトを取得
DOMImplementationLS impLS = (DOMImplementationLS) imp.getFeature("+LS", "3.0");
// LSInput クラスのインスタンスを取得
LSInput input = impLS.createLSInput();
// 読み込み元の XML として文字列を設定
input.setStringData(
"<test>1 つ目のテキストノード<t2>2 つ目のテキストノード</t2></test>" );
// LSParser クラスのインスタンス取得
LSParser parser = impLS.createLSParser(
DOMImplementationLS.MODE_SYNCHRONOUS, null );
// XML をパースして DOM Document オブジェクトを取得
Document doc = parser.parse( input );

// ===== XPath 式を実行 =====
// XPath 機能を持つオブジェクトを取得
XPathEvaluator evaluator = (XPathEvaluator) imp.getFeature("+XPath", "3.0");
// XPath 式を表すオブジェクトを生成
XPathExpression exp =
evaluator.createExpression( "string(//text())", null );
// XPath 式を実行し, 結果を取得
// 結果は第 2 引数で指定した型に自動的に変換される
// 変換しない場合は XPathResult.ANY_TYPE を指定すればよい
XPathResult res =
(XPathResult) exp.evaluate( doc, XPathResult.STRING_TYPE, null );
// 結果の文字列値を取得し, 表示
System.out.println( res.getStringValue() );
// => "1 つ目のテキストノード" と表示されるはず

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

解説

DOM 実装の取得

// Load and Save 機能および XPath 機能を実装しているか, 
// それらの機能と協調できる DOM 実装を取得
DOMImplementation imp = dir.getDOMImplementation("+LS 3.0 +XPath 3.0");

まずここで、"LS" (Load and Save) および "XPath" の機能を実装しているか、またはそれらの機能と協調できる DOM 実装を取得しています。 "LS" および "XPath" の前に "+" が付いていますが、これは 「DOM 実装自身が "LS" や "XPath" の機能を実装していなくても、協調できれば良い」 ということを表しています。 "+" を付けずに

DOMImplementation imp = dir.getDOMImplementation("LS 3.0 XPath 3.0");
とした場合は、DOM 実装自身が "LS" と "XPath" の機能を実装していなければなりませんので、敷居が高くなります。 私の環境の場合、"+" を付けなければ該当する DOM 実装はありませんでした。 基本的には機能名の前には "+" を付けると良いでしょう。

なお、機能名の後ろに "3.0" とありますが、これはその機能の版を表しています。 現在のところ "LS" と "XPath" の版は "3.0" のみです。

XML 文書の読み込み

XML 文書の読み込みに関しては 昨日の記事 を参照してください。 また、W3C DOM 3 Load and Save Specification も参照してください。

XPath 機能を持つオブジェクトを取得

// XPath 機能を持つオブジェクトを取得
XPathEvaluator evaluator = (XPathEvaluator) imp.getFeature("+XPath", "3.0");

ここで、DOM 実装から XPath 機能を持つオブジェクト (XPathEvaluator インターフェイスを実装しているオブジェクト) を取得しています。 getFeature メソッドでは、第 1 引数に機能名、第 2 引数にその版を指定します。 本来は第 1 引数の機能名の前に "+" を付ける必要はないはずなのですが、何故か私の環境では "+" を付けなければ null が返されました。 おそらくバグだと思われます。

XPath 式を表すオブジェクトを取得

// XPath 式を表すオブジェクトを生成
XPathExpression exp = evaluator.createExpression( "string(//text())", null );

次に createExpression メソッドを使って XPath 式を表すオブジェクト (XPathExpression インターフェイスを実装しているオブジェクト) を取得します。 第 1 引数が XPath 式 (文字列) で、第 2 引数は名前空間を解決するためのオブジェクトです。 名前空間を考えなくて良い場合は null で構いません。

XPath 式の実行

// XPath 式を実行し, 結果を取得
XPathResult res = (XPathResult) exp.evaluate( doc, XPathResult.STRING_TYPE, null );

ここで実際に XPath 式を実行します。 第 1 引数が XPath 式を評価するときの context node です。 XPath 式の中で相対パスを使っている場合は context node は重要になりますが、絶対パスを使っている場合は Document ノードで良いでしょう。 第 2 引数は、XPath 式の実行結果をどの型で評価するかを指定します。 ここで型を指定しておけば、自動的にその型に変換されます。 (今回の場合は、XPath 式の実行結果は String 型で、第 2 引数の指定も String 型なので変換されない。) 自動的な型変換をして欲しくない場合は XPathResult.ANY_TYPE を指定します。 第 3 引数は null で良いでしょう。

第 2 引数として指定できる定数にどのようなものがあるかは Interface XPathResult のドキュメントページ をご覧ください。

実行結果の取得

// 結果の文字列値を取得し, 表示
System.out.println( res.getStringValue() );

実行結果は (XPathResult インターフェイスを実装している) res に格納されています。 ここから値を取り出すためには各種 getter を使用します。 今回の場合は結果として文字列が得られていますので、getStringValue メソッドを使用してその文字列を取得できます。

その他にどのようなメソッドがあるかは Interface XPathResult のドキュメントページ をご覧ください。