--- title: Introduction to using XPath in JavaScript slug: Introduction_to_using_XPath_in_JavaScript tags: - Add-ons - DOM - Extensions - Transforming_XML_with_XSLT - Web Development - XML - XPath - XSLT ---

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

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

{{ 英語版章題("document.evaluate") }}

document.evaluate

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

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

{{ 英語版章題("Parameters") }}

パラメータ

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

{{ 英語版章題("Return Value") }}

戻り値

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

{{ 英語版章題("Implementing a Default Namespace Resolver") }}

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

名前空間リゾルバを作成するには、普通は 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 ではない要素や属性にマッチさせるには、接頭辞付きの名前テストを使い、その接頭辞を名前空間にマッピングする名前空間リゾルバを作成する必要があります。詳しくは下記のユーザ定義の名前空間リゾルバを作成する方法を参照して下さい。

{{ 英語版章題("Specifying the Return Type") }}

戻り値の型を指定する

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

{{ 英語版章題("Simple Types") }}

単純型

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

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

{{ 英語版章題("Example") }}

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

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

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

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

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

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

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

{{ 英語版章題("Node-Set Types") }}

ノード集合型

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

{{ 英語版章題("Iterators") }}

イテレータ

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

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

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

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

{{ 英語版章題("Iterator Example") }}

イテレータの使用例
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 );
}

{{ 英語版章題("Snapshots") }}

スナップショット

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

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

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

{{ 英語版章題("Snapshot Example") }}

スナップショットの使用例
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 );
}

{{ 英語版章題("First Node") }}

ファーストノード

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

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

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

{{ 英語版章題("First Node Example") }}

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

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

{{ 英語版章題("The ANY_TYPE Constant") }}

ANY_TYPE 定数

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

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

評価の後に型を判断するには、XPathResult オブジェクトの resultType プロパティを使います。このプロパティの定数値は付録に記載されています。 None Yet {{ 英語版章題("Any_Type Example") }}

Any_Type Example

{{ 英語版章題("Examples") }}

{{ 英語版章題("Within a HTML Document") }}

HTML 文書内で

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

XPath を使って HTML 文書内の全ての <h2> 見出し要素を抽出したければ、xpathExpression は単に '//h2' となります。 // は再帰下降演算子 (Recursive Descent Operator)なので、この式は文書ツリー内のあらゆる位置にある、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 が返されます。

{{ 英語版章題("Evaluating against an XML document within an Extension") }}

拡張機能内の 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 );

{{ 英語版章題("Appendix") }}

付録

{{ 英語版章題("Implementing a User Defined Namespace Resolver") }}

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

この例は説明のためだけのものです。 この関数は、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 );

{{ 英語版章題("Implementing a default namespace for XML documents") }}

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 Defined Constants") }}

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 文書内で式にマッチした最初のノードを格納した結果ノード集合。

{{ 英語版章題("Original Document Information") }}

原文情報