XPath

revised: Nov./16th/2003

XPathの概要

XPath (XML Path Language)は、XML 文書の中から、条件に適合した部分の集合を取り出すための仕様です。

1999年11月にW3Cより、XSLTと同時に勧告されました。XPath自身は、XML文書ではなく、XML 文書の一部分として働くものです。

XPathでは、要素や属性の部分をノードと呼びます。XML文書を、ルート・ノードを頂点とするツリー構造のモデルとして扱います。基本的には、UNIX 系の階層型ファイル・システムのパスと同じように指定しますが、関数や式を組み合わせて複雑な指定が可能となっています。

XPathで特徴的なのは、URLやファイルシステムのパスのように、特定のものを指定するものではないということです。むしろ、RDBに対するSQLのように、条件に適合するノードの集合を指定するものです。例えば、次のように指定した場合、XPathプロセッサは、XML 文書の、ルート・ノードの子ノードである全てのelem1要素の集合を返します。

/elem1

ノード

ルート・ノードはスラッシュ(/)で表現し、XML文書のルート要素(ドキュメント要素)は、ルート・ノードの子になります。その子である要素、属性、テキストなどのノードは、UNIX系のファイル・システムのパスと同様に、スラッシュで区切って階層を明示して参照します。その他の各ノード間は、親、子、兄弟などの関係を持つことになります。表1に、XPathで紹介されている7つのノードの種類を示しました。

表1. XPathのノードの種類
ノードの種類概要
ルート・ノードツリー構造の頂点なしルート要素ノード、処理命令ノード、コメント・ノード
要素ノード要素要素ノード、ルート・ノード要素ノード、コメント・ノード、処理命令ノード、テキスト・ノード
属性ノード属性要素ノード(但し要素ノードの子ではない)なし
名前空間ノード名前空間要素ノード(但し要素ノードの子ではない)なし
処理命令ノード処理命令要素ノードなし
コメント・ノードコメント要素ノードなし
テキスト・ノード要素の内容文字列要素ノードなし

これらのノードはDOMノードとは異なることに注意が必要です。

属性ノード(attribute node)は、各々が属する要素に対応する要素ノード(element node)を親とするが、当該要素ノードの子ではありません。属性ノードは、開始タグで明示的に指定された属性及び、デフォルト値を持つ明示的に指定されていない属性についてのみ、各々が属する要素を親ノードとして、ツリー構造中の属性ノードとなります。子要素にも意味が継承される属性の場合は、明示的に指定された親要素のみ、属性ノードが存在し、継承する子要素に明示的に指定されていなければ、当該属性のノードを持つことはありません。

名前空間ノード(namespace node)も、要素を親ノードとするが、その子ではありません。名前空間ノードは、要素ノードに対して、当該要素ノード及びその子孫のノードの名前空間毎に一個ずつ結び付けられます。

処理命令(processing instruction;PI)は、<?処理系 xxxxx xxxxx?>というい書式で、指定した処理系の動作を明示的に指定するものです。処理系側に受け取れるインタフェースが備わっていることが前提となり、XMLプロセッサは、未知の処理命令を無視することで、XML文書のポータビリティに影響が及ばないように振舞います。任意の場所に記述することが可能で、出現場所によって、ルート・ノード又は適当な要素ノードと親子関係をもちます。XML文書の冒頭に記述するXML宣言も、XML処理系に対する処理命令の一つだと考えることができますが、XPathではXML宣言は処理命令とは考えないので、XML宣言に対する処理命令ノードは存在しません。

ロケーション・パス

XPathで、XML文書のノードを指定するものを、ロケーション・パスと呼びます。

ロケーションパスは、スラッシュで区切ってノード階層を表現します。ルート・ノードから指定する絶対ロケーションパスと、任意のノードを起点とする相対ロケーション・パスの二つに分かれます。起点とするノードのことをコンテキスト・ノード(文脈ノード)と呼び、絶対ロケーション・パスは、コンテキスト・ノードがルート・ノードであるロケーション・パスということになります。

次のXML文書を例にとって、ロケーションパスを記述して見ましょう。

リスト1. XML文書のサンプル(XPathDemo.xml)

<?xml version="1.0" encoding="UTF-8"?>
<address>
    <item sex="male" custid="E21099">
        <name>菅井 学</name>
        <access kind="email"></access>
        <access kind="url">http://www.nextindex.net/java/</access>
        <image file="msugai.png" />
    </item>
    <item sex="male" custid="E27989">
        <name>鈴木 竜広</name>
        <access kind="email">tsuzuki@hoge.foo.bar</access>
        <image file="tsuzuki.png" />
    </item>
    <item sex="female" custid="E29435">
        <name>栃原 宏枝</name>
        <access kind="tel">090-xxxx-xxxx</access>
        <image file="tochi.png" />
    </item>
</address>

リスト1のitem要素の配下のaccess要素を指し示すための絶対ロケーション・パスの記述は次のようになります。

/address/item/access

この結果、リスト1の例では、マッチするのは4つのaccess要素全てとなります。ルートとその全ての子孫ノードのaccess要素にマッチするためには、次のように記述することもできます。

//access

ファイルパスと同様に、ワイルドカードにアスタリスク(*)を指定することも可能です。次のXPathはaddress要素の全ての子要素にマッチします。

/address/*

従って、"//*"とすれば、全ての要素にマッチすることになります。

リスト2では、XPathを実際に利用するためのコードを、JAXPによるDOMベースのコードで示しました。

リスト2. XPathのテスト用のコード(XPathDemo.java)

import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.apache.xpath.*;
class XPathDemo {
    public static void main(String[] args) {
        try {
            // DOMパーサ用ファクトリの生成
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            // 名前空間を認識する
            factory.setNamespaceAware(true);
            // DOM Documentインスタンス用ファクトリの生成
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 解析とDocumentインスタンスの取得
            Document doc = builder.parse(args[0]);
            
            // NodeIteratorの取得
            NodeIterator nl = XPathAPI.selectNodeIterator(doc, args[1]);
            
            int count = 0;
            Node n;
            while ((n = nl.nextNode()) != null) {
                count++;
            }
            System.out.println("counts: " + count);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

リスト2では、前半部分でDOMツリーを構築しています。DOMツリー全体を現すDocumentノードのオブジェクトdocを生成するところまでは先のページで紹介しました。ここでは、簡便のために属性のセットやHandlerの設定などは省略してありますが、テストのためには問題なく動作します。必要があれば、適宜追加してください。

DOMパーサの設定の次が、XPath用APIのためのコードです。XPathの低水準APIであるorg.apache.xpath.XPathAPIのstaticメソッドselectNodeIterator()の引数に、Documentノードのオブジェクトdocと、XPathを受け取るコマンド・ライン引数aregs[1]を渡し、org.w3c.dom.traversal.NodeIteratorオブジェクトを生成します。XPathは他の使用の部品として利用することになるため、この例のように、剥き出しで機能をつかうことは稀でしょうが、クラスXPathAPIに用意された少数のメソッドを使えば、ちょっとしたことには便利です。

クラスNodeIteratorは、引数にコンテキスト・ノードとXPathを受け取って、マッチするノードのセットを返すイタレータ(反復子)となります。メソッドnextNode()やpreviousNode()で前後の要素に移動し、使い方はリスト3に挙げたとおりだ。コレクション・インタフェースのjava.util.Iteratorなどのイタレータとは異なり、前後の要素の存在判定と移動が同時に行われることに注意が必要です。

リスト2の"XPathDemo.java"とリスト1のXML文書"XPathDemo.xml"を同じ場所に保存して実行してみましょう。但し、"XPathDemo.xml"は、UTF-8で保存する必要があります。他の文字符号化方法を使う場合は、XML宣言を適宜書き換えなければなりません。

実行結果は次のようになります。XML文書やXPathを適当に書き換えて、動作を確認してください。第一引数にはXML文書のパスを指定し、第二引数がXPathを指定します。

>javac XPathDemo.java
>java XPathDemo XPathDemo.xml /address/item/access
counts: 4
>java XPathDemo XPathDemo.xml //access
counts: 4
>java XPathDemo XPathDemo.xml /address/*
counts: 3
>java XPathDemo XPathDemo.xml //*
counts: 14

ロケーション・ステップ

XPathのスラッシュで括られた部分のことをロケーション・ステップと呼びます。先の例では、単純に要素名だけを記述しましたが、ロケーションステップには、次の情報を含めることで、よりきめ細かな指定が可能となります。

ロケーションステップは次の要領で記述します。

軸::ノード・テスト[述語]

先に挙げたロケーションパスは、これらの指定を省略した記法です。例えば、"/address/item/access"は、省略しないで書くと、次のようになります。

/child::address/child::address/child::access

ロケーション・ステップで、ノードのツリー構造における関係を示すのが軸(axes)と呼ばれる部分です。軸には表2に挙げた何れかを記述します。

表2. 軸の種類
ancestor自分の祖先ノード
ancestor-or-self自分自身とその祖先
attribute自分の属性ノード
child子ノード
descendant自分の子孫ノード
descendant-or-self自分自身とその子孫
following後続する全てのノード
following-sibling自分より後続の兄弟
namespace自分の名前空間ノード
parent親ノード
preceding先行する全てのノード
preceding-sibling自分より先行する兄弟
self自分自身

表2を使うと、あまりに仰々しいので、最初の例に挙げたように、これらを省略して記述することができます(表3)。例えば、先に挙げた//descendant-or-selfを意味する省略記法です。

表3. 軸の省略記法
省略記法省略しない記法
何も記述しないchild
@attribute
.self
..parent
//descendant-or-self

例えば、リスト1 (XPathDemo.xml)で、item要素のsex属性を指定するには、次の何れかの記述が可能です。

/child::address/child::item/attribute::sex

又は、

/address/item/@sex

ノード・テスト

今までは、ノード・テストには要素名や属性名を単純に記述していました。他にも表4に挙げるような関数を使うことができます。関数とは云えませんが、先に例で挙げたワイルドカード*もノード・テストの特殊なものの一つです。

表4. ノード・テストの関数
comment()コメント・ノード
text()テキスト・ノード
processing-instruction()処理命令ノード
node()ノード・タイプに寄らない全てのノード
*軸の指定するノード・タイプにマッチする全てのノード

例えば、リスト1のXML文書でname要素のテキスト・ノードを参照するには、次のように記述します。

/address/item/name/text()

述部

ノード・テストでマッチしたノードの集合に対して、関数や演算子を用いてさまざまな指定を記述するのが述部です。一般に、XPathを評価した結果は、オブジェクトと呼ばれ、クラスXPathAPIのメソッドeval()によって、四つの型が返されます。

org.apache.xpath.objects.XObject
    |
    +--org.apache.xpath.objects.XNodeSet
    |
    +--org.apache.xpath.objects.XString
    |
    +--org.apache.xpath.objects.XBoolean
    |
    +--org.apache.xpath.objects.XNumber

リスト3は、第一引数にXML文書のパス、第二引数にXPathを指定して、評価結果に応じた型を出力するものです。

リスト3. XPathのノード・タイプを出力(XPathTypeDemo.java)

import java.io.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
import org.apache.xpath.*;
import org.apache.xpath.objects.*;
class XPathTypeDemo {
    public static void main(String[] args) {
        try {
            // DOMパーサ用ファクトリの生成
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true);
            // 名前空間を認識する
            factory.setNamespaceAware(true);
            // DOM Documentインスタンス用ファクトリの生成
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 解析とDocumentインスタンスの取得
            Document doc = builder.parse(args[0]);
            
            // NObjectの取得
            XObject xobj = XPathAPI.eval(doc, args[1]);
            
            System.out.println(xobj.getTypeString());
            if (xobj instanceof XNodeSet) {
                System.out.println(args[1] + " is XNodeSet.");
            } else if (xobj instanceof XString) {
                System.out.println(args[1] + " is XString.");
            } else if (xobj instanceof XBoolean) {
                System.out.println(args[1] + " is XBoolean.");
            } else if (xobj instanceof XNumber) {
                System.out.println(args[1] + "is XNumber.");
            } else {
                System.out.println(args[1] + "is unkown.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

例えば、ロケーション・パスを第二引数に与えればXNodeSetが返されます。同様に、ノードを文字列に変換する関数string()を指定すればXStringが返され、ノードに含まれるノードの総数を返すlast()を指定すればXNumberが返されることになります。

>javac XPathTypeDemo.java
>java XPathTypeDemo XPathDemo.xml /address/item
#NODESET
/address/item is XNodeSet.
>java XPathTypeDemo XPathDemo.xml string()
#STRING
string() is XString.
>java XPathTypeDemo XPathDemo.xml last()
#NUMBER
last()is XNumber.

string()やlast()以外にも、XPathでは、これらの型に応じて、ロケーション・ステップで利用するための、多くの関数が用意されています。表5と6に、利用できる代表的な関数と演算子を載せました。より詳細な定義はW3CのXPath 1.0仕様書をご参照ください。

次に挙げるのは、XPath仕様で定義されている関数のうち、比較的良く使うであろう物です。

表5. XPath関数
ノード集合関数数値 last()適合したノードの数
数値 position()ノード集合の中のノード番号
数値 count(ノード集合)引数のノード集合の中のノード数
文字列 local-name(ノード集合?)引数のノード集合の最初のノードの局所名
文字列 namespace-uri(ノード集合?)引数のノード集合の最初のノードの名前空間URI
文字列 name(ノード集合?)引数のノード集合の最初のノードの名前空間修飾された展開名
文字列関数文字列 string(オブジェクト)引数に対応する文字列
文字列 concat(文字列,文字列,文字列*)引数の文字列を結合した文字列
ブーリアン start-with(文字列,文字列)第一引数の文字列が第二引数の文字列で始まる場合にtrueを返す
ブーリアン contains(文字列,文字列)第一引数の文字列が第二引数の文字列を含む場合にtrueを返す
文字列 substring(文字列,数値,数値?)第一引数の文字列の、第二引数の箇所で始まり、第三引数の長さの部分文字列
数値 string-length(文字列?)引数の文字列の長さ
文字列 normalize-space(文字列)引数の文字列の前後の空白を除去し、連続する空白を一つにまとめた文字列
ブーリアン関数ブーリアン boolean(オブジェクト?)引数に対応するブーリアン値
ブーリアン not(ブーリアン)引数の真に対して偽を、偽に対して真を返す
ブーリアン true()常に真を返す
ブーリアン false()常に偽を返す
数値関数数値 number(オブジェクト?)引数に対応した数値を返す
数値 sum(ノード集合)引数のノード集合を文字列に変換した結果を数値に変換し、その和を返す
数値 floor(数値)引数以下の最大の整数を返す
数値 ceiling(数値)引数以上の最小の整数を返す
数値 round(数値)引数を四捨五入した整数を返す

次に挙げるのは、XPathの評価式中で利用可能な演算子の一覧です。

表6. XPath演算子
ANDかつ
ORまたは
=等号
!=不等号
&lt;小なり
&lt;=小なりイコール
&gt;大なり
&gt;=大なりイコール

例えば、リスト1のXML文書で、「sex="male"であるitem要素の子要素であるname要素の内容文字列」を指し示すには、次のように記述します。

/address/item[@sex='male']/name/text()

仕様としては二重引用符でも良いはずですが、Xalanは一重引用符でなければマッチしたとみなしません。XSLTでは、属性matchやselectといった、XSLT文書内の要素の、なんらかの属性の値として、専ら二重引用符の中で使うのであれば、一重引用符を使う必要があります。

address要素の最後のitem子要素のaccess要素は次のように指定します。

/address/item[position()=last()]/access

又は、

/address/item[last()]/access


Copyright © 2003 SUGAI, Manabu. All Rights Reserved.
SEO [PR] !uO z[y[WJ Cu