Introduction to using XPath in JavaScript

この文書では拡張機能や Web サイトから JavaScript 内で XPath を使うためのインターフェースについて解説します。Mozilla は DOM 3 XPath のかなりの部分を実装しており、HTML 文書と XML 文書の双方に対して XPath 式を実行することができます。

XPath を利用するための主となるインターフェースは document オブジェクトの evaluate 関数です。

document.evaluate

このメソッドは HTML を含む XML ベースの文書に対して XPath 式を評価し、XPathResult オブジェクトを返します。 XPathResult オブジェクトは単一のノード、もしくはノードの集合になります。このメソッドの情報は DOM:document.evaluate にありますが、このメソッドの解説のためには内容が薄いため、以下でさらに詳しく説明します。

var xpathResult = document.evaluate( xpathExpression, contextNode, namespaceResolver, resultType, result );

パラメータ

evaluate 関数は 5 つのパラメータを取ります。

  • xpathExpression: 評価する XPath 式を文字列で指定します。
  • contextNode: xpathExpression を評価する対象となる文書内のノードを指定します。指定されたノードの全ての子ノードに対しても評価が行われます。もっともよく指定される値は document です。
  • namespaceResolver: xpathExpression に含まれるあらゆる名前空間接頭辞を渡され、その接頭辞に対応する名前空間 URI を表す文字列を返す関数です。この関数により、XPath 式で使われている接頭辞と文書内で使われている接頭辞が異なっていたとしてもそれを変換する事が可能になります。この関数は次のいずれかです。
    • XPathEvaluator オブジェクトの createNSResolver メソッドにより作成されたもの。ほとんどの場合はこれを使うべきでしょう。
    • HTML 文書の場合や、名前空間接頭辞が使われていない場合は nullxpathExpression に名前空間接頭辞が含まれている場合に null を使うと、NAMESPACE_ERR コードと共に DOMException が投げられるので注意してください。
    • ユーザ定義のカスタム関数。詳しくは付録のユーザ定義名前空間リゾルバの使用の節を参照して下さい。
  • resultType: 評価の結果返してほしい値の型を示す定数です。もっとも良く指定される定数は XPathResult.ANY_TYPE で、この場合、指定された XPath 式に対して一番適切な型で結果が返されます。指定できる定数の一覧は付録の定数一覧の節を参照してください。それぞれの定数の使い方は戻り値の型の指定の節を参考にしてください。
  • result: 既存の XPathResult オブジェクトまたは null を指定します。 XPathResult オブジェクトが指定された場合には、そのオブジェクトが再利用されます。 null が指定された場合には新しい XPathResult オブジェクトが生成されます。

戻り値

パラメータ resultType指定された型の XPathResult オブジェクトを返します。XPathResult インターフェースはここで定義されています。

デフォルト名前空間リゾルバの実装

名前空間リゾルバを作成するには、普通は document オブジェクトの createNSResolver メソッドを使います。

var nsResolver = document.createNSResolver( contextNode.ownerDocument == null ? contextNode.documentElement : contextNode.ownerDocument.documentElement );

Or alternatively by using the <code>createNSResolver</code> method of a <code>XPathEvaluator</code> object. <pre> var xpEvaluator = new XPathEvaluator(); var nsResolver = xpEvaluator.createNSResolver( contextNode.ownerDocument == null ? contextNode.documentElement : contextNode.ownerDocument.documentElement ); </pre> それから 変数 nsResolver を パラメータ namespaceResolver として document.evaluate に渡します。

注意すべきなのは、XPath では接頭辞のない QName は名前空間が null の要素にのみマッチすると定義されているという点です。XPath にはデフォルト名前空間を取得する手段はありません。名前空間が null ではない要素や属性にマッチさせるには、接頭辞付きの名前テストを使い、その接頭辞を名前空間にマッピングする名前空間リゾルバを作成する必要があります。詳しくは下記のユーザ定義の名前空間リゾルバを作成する方法を参照して下さい。

戻り値の型を指定する

document.evaluate から返される変数 xpathResult は、単一のノード (単純型) もしくはノードのコレクション (ノード集合型) から成ります。

単純型

resultType で要求された結果型が次のうちのどれかであった場合、

  • NUMBER_TYPE - 倍精度浮動小数点数 (double)
  • STRING_TYPE - 文字列
  • BOOLEAN_TYPE - 真偽値

それぞれ以下の XPathResult オブジェクトのプロパティにアクセスする事で式の戻り値を得る事ができます。

  • numberValue
  • stringValue
  • booleanValue

下の例では XPath 式 count(//p) によって HTML 文書内の <p> 要素の数を取得しています。

var paragraphCount = document.evaluate( 'count(//p)', document, null, XPathResult.ANY_TYPE, null );

alert( 'この文書には ' + paragraphCount.numberValue + ' 個の段落要素が含まれています' );

JavaScript では数値を表示しようとすると文字列に変換されますが、XPath インターフェイスは stringValue プロパティを要求しても数値の結果を自動的に変換しないので、下のコードは<big>動作しません</big>

var paragraphCount = document.evaluate('count(//p)', document, null, XPathResult.ANY_TYPE, null );

alert( 'この文書には ' + paragraphCount.stringValue + ' 個の段落要素が含まれています' );

これを実行すると NS_DOM_TYPE_ERROR コードの例外が返されます。

ノード集合型

XPathResult オブジェクトが返すノード集合には主として 3 種類の型があります。

イテレータ

パラメータ resultType で指定された結果型が次のどちらかの場合、

  • UNORDERED_NODE_ITERATOR_TYPE
  • ORDERED_NODE_ITERATOR_TYPE

マッチしたノードのノード集合がXPathResult オブジェクトとして返されます。これはイテレータのようにふるまい、 XPathResultiterateNext() メソッドを使ってその中に含まれる個々のノードにアクセスできます。

マッチしたノードに対する反復が全て終了すると、iterateNext()null を返します。

ただし、反復処理中に文書が変異した (文書ツリーが改変された) 場合、反復処理は無効化され、XPathResultinvalidIteratorState プロパティが true に設定され、NS_ERROR_DOM_INVALID_STATE_ERR 例外が投げられます。

イテレータの使用例
var iterator = document.evaluate('//phoneNumber', documentNode, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null );

try {
  var thisNode = iterator.iterateNext();
  
  while (thisNode) {
    alert( thisNode.textContent );
    thisNode = iterator.iterateNext();
  }	
}
catch (e) {
  dump( 'Error: Document tree modified during iteraton ' + e );
}

スナップショット

パラメータ resultType で指定された結果型が次のどちらかの場合、

  • UNORDERED_NODE_SNAPSHOT_TYPE
  • ORDERED_NODE_SNAPSHOT_TYPE

返される XPathResult オブジェクトはマッチしたノードの静的なノード集合となり、XPathResult オブジェクトの snapshotItem(itemNumber) メソッドによってそれぞれのノードにアクセス出来ます。itemNumber は取り出すノードのインデックスです。含まれるノードの総数は snapshotLength プロパティから得られます。

スナップショットは文書が変異しても変更されず、イテレータと違って無効になることはありませんが、スナップショットは現在の文書に対応しません。ノードが移動されていたり、既に存在しないノードが含まれていたり、新しいノードが追加されている可能性もあります。

スナップショットの使用例
var nodesSnapshot = document.evaluate('//phoneNumber', documentNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );

for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ )
{
  dump( nodesSnapshot.snapshotItem(i).textContent );
}

ファーストノード

パラメータ resultType で指定された結果型が次のどちらかの場合、

  • ANY_UNORDERED_NODE_TYPE
  • FIRST_ORDERED_NODE_TYPE

XPath 式にマッチした最初のノードのみが XPathResult オブジェクトとして返されます。このノードには XPathResult オブジェクトの singleNodeValue プロパティによってアクセスできます。ノード集合が空ならばこのプロパティは null になります。

ただし、ordered サブタイプの場合は文書順において最初にマッチしたノードであることが保証されますが、unordered サブタイプの場合、返される単一のノードは文書順において最初のものではない可能性があるので注意が必要です。

ファーストノードの使用例
var firstPhoneNumber = document.evaluate('//phoneNumber', documentNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null );

dump( 'The first phone number found is ' + firstPhoneNumber.singleNodeValue.textContent );

ANY_TYPE 定数

パラメータ resultType に指定された結果型が ANY_TYPE である場合、返される XPathResult オブジェクトは、式を評価した結果から導き出される適切な型になります。

返される結果型は単純型 ( NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE ) のうちのいずれにもなり得ます<big>が</big>、もしノード集合であった場合には、UNORDERED_NODE_ITERATOR_TYPE<big>しか</big>なり得ません。

評価の後に型を判断するには、XPathResult オブジェクトの resultType プロパティを使います。このプロパティの定数値は付録に記載されています。 None Yet

Any_Type Example

HTML 文書内で

下のコードは、 XPath 式を評価する対象となる HTML 文書の内部や、それにリンクされた JavaScript 内に設置するためのものです。

XPath を使って HTML 文書内の全ての <h2> 見出し要素を抽出したければ、xpathExpression は単に '//h2' となります。 //再帰下降演算子なので、この式は文書ツリー内のあらゆる位置にある、nodeName が h2 である要素にマッチします。 link to introductory xpath doc

var headings = document.evaluate('//h2', document, null, XPathResult.ANY_TYPE, null );

HTML は名前空間を持っていないため、パラメータ namespaceResolver には null を渡している事に注目してください。

文書全体から見出し要素を探すため、ここでは document オブジェクト自体を contextNode として使っています。

この式の結果は XPathResult オブジェクトです。返された結果の型を知りたければ、返されたオブジェクトの resultType プロパティを評価します。この場合は 4 、つまり UNORDERED_NODE_ITERATOR_TYPE と評価されるでしょう。これは XPath 式の結果がノード集合であった場合のデフォルトの結果型です。この型はノードに一つずつアクセスする事ができ、返されるノードの順序は決まっていません。返されたノードにアクセスするには、返されたオブジェクトの iterateNext() メソッドを使います。

var thisHeading = headings.iterateNext();

var alertText = 'この文書内のレベル 2 の見出しは、\n'

while (thisHeading) {
  alertText += thisHeading.textContent + '\n';
  thisHeading = headings.iterateNext();
}

反復によってノードを得られれば、そのノードの全ての標準 DOM インターフェイスにアクセスできます。式によって返される h2 要素に対する反復処理が全て終了すると、それ以降は iterateNext() を何度呼び出しても null が返されます。

拡張機能内の XML 文書に対して評価する

例として XML 文書が chrome://yourextension/content/peopleDB.xml にあるとします。

<?xml version="1.0"?>
<people xmlns:xul = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
  <person>
	<name first="george" last="bush" />
	<address street="1600 pennsylvania avenue" city="washington" country="usa"/>
	<phoneNumber>202-456-1111</phoneNumber>
  </person>
  <person>
	<name first="tony" last="blair" />
	<address street="10 downing street" city="london" country="uk"/>
	<phoneNumber>020 7925 0918</phoneNumber>
  </person>
</people>

拡張機能内で XML 文書の内容を取得できるようにするため、XMLHttpRequest オブジェクトを作成して文書を同期的に読み込みます。変数 xmlDoc には文書が XMLDocument オブジェクトとして格納されるので、それに対して evaluate メソッドを使う事ができます。

拡張機能の xul/js 文書で使用する JavaScript は以下の通りです。

var req = new XMLHttpRequest();

req.open("GET", "chrome://yourextension/content/peopleDB.xml", false); 
req.send(null);

var xmlDoc = req.responseXML;		

var nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement);

var personIterator = xmlDoc.evaluate('//person', xmlDoc, nsResolver, XPathResult.ANY_TYPE, null );

付録

ユーザ定義の名前空間リゾルバの実装

この例は説明のためだけのものです。 この関数は、xpathExpression から名前空間接頭辞を取り、その接頭辞に対応する URI を返さなければなりません。例えば、この式は、

'//xhtml:td/mathml:math'

(X)HTML のテーブルデータセル要素の子要素である全ての MathML 式を選択します。

接頭辞 'mathml:' と 名前空間 URI 'http://www.w3.org/1998/Math/MathML' を、接頭辞 'xhtml:' と URI 'http://www.w3.org/1999/xhtml' をそれぞれ関連付けるため、関数を用意します。

function nsResolver(prefix) {
  var ns = {
    'xhtml' : 'http://www.w3.org/1999/xhtml',
    'mathml': 'http://www.w3.org/1998/Math/MathML'
  };
  return ns[prefix] || null;
}

そうすると document.evaluate をこのようにして呼び出せます。

document.evaluate( '//xhtml:td/mathml:math', document, nsResolver, XPathResult.ANY_TYPE, null );

XML 文書のデフォルト名前空間を実装する

デフォルト名前空間リゾルバの実装で述べたように、デフォルトリゾルバは XML 文書のデフォルト名前空間を処理しません。たとえばこの文書では、

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <entry />
    <entry />
    <entry />
</feed>

doc.evaluate('//entry', doc, nsResolver, XPathResult.ANY_TYPE, null) は、nsResolvercreateNSResolver によって返されたリゾルバである場合、空集合を返します。リゾルバとして null を渡しても同じです。

正しいデフォルト名前空間 (この場合は Atom 名前空間) を返すカスタムリゾルバを作成すれば、この問題を解決できます。この時、XPath 式の中ではなんらかの名前空間接頭辞を使わなければならないことに注意してください。これはリゾルバ関数がその接頭辞を指定した名前空間に変換できるようにするためです。例えばこのようにします。

function resolver() {
    return 'http://www.w3.org/2005/Atom';
}
doc.evaluate('//myns:entry', doc, resolver, XPathResult.ANY_TYPE, null)

文書で複数の名前空間が使われている場合には、より複雑なリゾルバが必要になります。

XPathResult の定義済み定数

定義済みの結果型定数 解説
ANY_TYPE 0 式の評価によって導き出される適切な型を格納した結果の集合。結果がノード集合ならば、結果の型は常に UNORDERED_NODE_ITERATOR_TYPE となるので注意が必要。
NUMBER_TYPE 1 一つの数値を格納した結果。 count() 関数を使用した XPath 式などで有用。
STRING_TYPE 2 一つの文字列を格納した結果。
BOOLEAN_TYPE 3 一つの真偽値を格納した結果。 not() 関数を使用した XPath 式などで有用。
UNORDERED_NODE_ITERATOR_TYPE 4 式にマッチした全てのノードを格納した結果ノード集合。ノードの順番は文書内に現れる順番と必ずしも一致しない。
ORDERED_NODE_ITERATOR_TYPE 5 式にマッチした全てのノードを格納した結果ノード集合。ノードの順番は文書内に現れる順番に一致する。
UNORDERED_NODE_SNAPSHOT_TYPE 6 式にマッチした全てのノードのスナップショットを格納した結果ノード集合。ノードの順番は文書内に現れる順番と必ずしも一致しない。
ORDERED_NODE_SNAPSHOT_TYPE 7 式にマッチした全てのノードのスナップショットを格納した結果ノード集合。ノードの順番は文書内に現れる順番に一致する。
ANY_UNORDERED_NODE_TYPE 8 式にマッチしたノードのうちのどれか一つを格納した結果ノード集合。これは必ずしも文書内で式にマッチした最初のノードというわけではない。
FIRST_ORDERED_NODE_TYPE 9 文書内で式にマッチした最初のノードを格納した結果ノード集合。

原文情報

  • 原文 Mozilla XPath Tutorial に基づいて作成されています。
  • 原文の著者: James Graham
  • その他の貢献者: James Thompson
  • 最終更新日: 2006 年 3 月 25 日

 

ドキュメントのタグと貢献者

 このページの貢献者: teoli, Mgjbot, Shoot, Potappo, Gakuueda
 最終更新者: teoli,