Using XPath

  • Revision slug: Using_XPath
  • Revision title: Using XPath
  • Revision id: 95437
  • Created:
  • Creator: kmaglione
  • Is current revision? No
  • Comment Remove spam for Sarissa library; 149 words removed

Revision Content

 

XPath is a language for addressing parts of an XML document. It is a W3C recommendation.

This article describes Mozilla interfaces exposing XPath functionality to JavaScript code. These are described in DOM Level 3 XPath (which is W3C Working Group Note at this moment).

This article does not attempt teach XPath itself. If you're unfamiliar with this technology, please refer to W3Schools XPath tutorial.

For a very simple XPath usage example, see: Code_snippets:HTML_to_DOM#Using_a_hidden_XUL_iframe_.28complete_example.29

Node-specific evaluator function

The following function can be used to evaluate XPath expressions on given XML nodes. The first argument is a DOM node or Document object, while the second is a string defining an XPath expression.

// Evaluate an XPath expression aExpression against a given DOM node
// or Document object (aNode), returning the results as an array
// thanks wanderingstan at morethanwarm dot mail dot com for the
// initial work.
function evaluateXPath(aNode, aExpr) {
  var xpe = new XPathEvaluator();
  var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
    aNode.documentElement : aNode.ownerDocument.documentElement);
  var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
  var found = [];
  var res;
  while (res = result.iterateNext())
    found.push(res);
  return found;
}

This function uses new XPathEvaluator(). That constructor is specific to Mozilla. Scripts used on a webpage which might be used by other browsers should instead replace the call to new XPathEvaluator() with the following fragment:

  // XPathEvaluator is implemented on objects that implement Document
  var xpe = aNode.ownerDocument || aNode;

In that case the creation of the XPathNSResolver can be simplified as:

  var nsResolver = xpe.createNSResolver(xpe.documentElement);

Note however that createNSResolver should only be used if you are sure the namespace prefixes in the XPath expression match those in the document you want to query (and that no default namespace is being used (though see document.createNSResolver for a workaround)). Otherwise, you have to provide your own implementation of XPathNSResolver.

If you are using XMLHttpRequest to read a local or remote XML file into a DOM tree (as described in Parsing and serializing XML), the first argument to evaluateXPath() should be req.responseXML.

Sample usage

Assume we have the following XML document (see also How to Create a DOM tree and Parsing and serializing XML):

<?xml version="1.0"?>
<people>
  <person first-name="eric" middle-initial="H" last-name="jung">
    <address street="321 south st" city="denver" state="co" country="usa"/>
    <address street="123 main st" city="arlington" state="ma" country="usa"/>
  </person>

  <person first-name="jed" last-name="brown">
    <address street="321 north st" city="atlanta" state="ga" country="usa"/>
    <address street="123 west st" city="seattle" state="wa" country="usa"/>
    <address street="321 south avenue" city="denver" state="co" country="usa"/>
  </person>
</people>

You can now "query" the document with XPath expressions. Although walking the DOM tree can achieve similar results, using XPath expressions is much quicker and more powerful. If you can rely on id attributes, document.getElementById() is still powerful, but it's not nearly as powerful as XPath. Here are some examples.

// display the last names of all people in the doc
var results = evaluateXPath(people, "//person/@last-name");
for (var i in results)
  alert("Person #" + i + " has the last name " + results[i].value);

// get the 2nd person node
results = evaluateXPath(people, "/people/person[2]");

// get all the person nodes that have addresses in denver
results = evaluateXPath(people, "//person[address/@city='denver']");

// get all the addresses that have "south" in the street name
results = evaluateXPath(people,  "//address[contains(@street, 'south')]");
alert(results.length);

docEvaluateArray

The following is a simple utility function to get (ordered) XPath results into an array, regardless of whether there is a special need for namespace resolvers, etc. It avoids the more complex syntax of document.evaluate() for cases when it is not required as well as the need to use the special iterators on XPathResult (by returning an array instead).

// Example usage:
// var els = docEvaluateArray('//a');
// alert(els[0].nodeName); // gives 'A' in HTML document with at least one link

function docEvaluateArray (expr, doc, context, resolver) {
	doc = doc ? doc : (context ? context.ownerDocument : document);
	resolver = resolver ? resolver : null;
	context = context ? context : doc; 
	
	var result = doc.evaluate(expr, context, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	var a = [];
	for(var i = 0; i < result.snapshotLength; i++) {
		a[i] = result.snapshotItem(i);
	}
	return a;
}

getXPathForElement

The following function allows one to pass an element and an XML document to find a unique string XPath expression leading back to that element.

function getXPathForElement(el, xml) {
	var xpath = '';
	var pos, tempitem2;
	
	while(el !== xml.documentElement) {		
		pos = 0;
		tempitem2 = el;
		while(tempitem2) {
			if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
				pos += 1;
			}
			tempitem2 = tempitem2.previousSibling;
		}
		
		xpath = "*[name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;

		el = el.parentNode;
	}
	xpath = '/*'+"[name()='"+xml.documentElement.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']"+'/'+xpath;
	xpath = xpath.replace(/\/$/, '');
	return xpath;
}

Resources

See also

{{ languages( { "fr": "fr/Utilisation_de_XPath", "ja": "ja/Using_XPath", "zh-cn": "cn/Using_XPath", "ko": "ko/Using_XPath" } ) }}

Revision Source

<p> </p>
<p><a href="/en/XPath" title="en/XPath">XPath</a> is a language for addressing parts of an XML document. It is a <a class="external" href="http://www.w3.org/TR/xpath">W3C recommendation</a>.</p>
<p>This article describes Mozilla interfaces exposing XPath functionality to JavaScript code. These are described in <a class="external" href="http://www.w3.org/TR/DOM-Level-3-XPath/">DOM Level 3 XPath</a> (which is W3C Working Group Note at this moment).</p>
<p>This article does not attempt teach XPath itself. If you're unfamiliar with this technology, please refer to <a class="external" href="http://www.w3schools.com/xpath/">W3Schools XPath tutorial</a>.</p>
<p>For a very simple XPath usage example, see: <a href="/en/Code_snippets/HTML_to_DOM#Using_a_hidden_XUL_iframe_.28complete_example.29" title="en/Code_snippets/HTML_to_DOM#Using_a_hidden_XUL_iframe_.28complete_example.29">Code_snippets:HTML_to_DOM#Using_a_hidden_XUL_iframe_.28complete_example.29</a></p>
<h3 id="Node-specific_evaluator_function" name="Node-specific_evaluator_function">Node-specific evaluator function</h3>
<p>The following function can be used to evaluate XPath expressions on given XML nodes. The first argument is a DOM node or Document object, while the second is a string defining an XPath expression.</p>
<pre class="brush: js">// Evaluate an XPath expression aExpression against a given DOM node
// or Document object (aNode), returning the results as an array
// thanks wanderingstan at morethanwarm dot mail dot com for the
// initial work.
function evaluateXPath(aNode, aExpr) {
  var xpe = new XPathEvaluator();
  var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ?
    aNode.documentElement : aNode.ownerDocument.documentElement);
  var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
  var found = [];
  var res;
  while (res = result.iterateNext())
    found.push(res);
  return found;
}
</pre>
<p>This function uses <code>new XPathEvaluator()</code>. That constructor is specific to Mozilla. Scripts used on a webpage which might be used by other browsers should instead replace the call to <code>new XPathEvaluator()</code> with the following fragment:</p>
<pre class="brush: js">  // XPathEvaluator is implemented on objects that implement Document
  var xpe = aNode.ownerDocument || aNode;
</pre>
<p>In that case the creation of the <a href="/en/DOM/document.createNSResolver" title="en/DOM/document.createNSResolver">XPathNSResolver</a> can be simplified as:</p>
<pre class="brush: js">  var nsResolver = xpe.createNSResolver(xpe.documentElement);
</pre>
<p>Note however that <code>createNSResolver</code> should only be used if you are sure the namespace prefixes in the XPath expression match those in the document you want to query (and that no default namespace is being used (though see <a href="/en/DOM/document.createNSResolver" title="en/DOM/document.createNSResolver">document.createNSResolver</a> for a workaround)). Otherwise, you have to provide your own implementation of XPathNSResolver.</p>
<p>If you are using <a href="/en/XMLHttpRequest" title="en/XMLHttpRequest">XMLHttpRequest</a> to read a local or remote XML file into a DOM tree (as described in <a href="/en/Parsing_and_serializing_XML" title="en/Parsing_and_serializing_XML">Parsing and serializing XML</a>), the first argument to <code>evaluateXPath()</code> should be <code>req.responseXML</code>.</p>
<h4 id="Sample_usage" name="Sample_usage">Sample usage</h4>
<p>Assume we have the following XML document (see also <a href="/en/How_to_create_a_DOM_tree" title="en/How_to_create_a_DOM_tree">How to Create a DOM tree</a> and <a href="/en/Parsing_and_serializing_XML" title="en/Parsing_and_serializing_XML">Parsing and serializing XML</a>):</p>
<pre class="brush: xml">&lt;?xml version="1.0"?&gt;
&lt;people&gt;
  &lt;person first-name="eric" middle-initial="H" last-name="jung"&gt;
    &lt;address street="321 south st" city="denver" state="co" country="usa"/&gt;
    &lt;address street="123 main st" city="arlington" state="ma" country="usa"/&gt;
  &lt;/person&gt;

  &lt;person first-name="jed" last-name="brown"&gt;
    &lt;address street="321 north st" city="atlanta" state="ga" country="usa"/&gt;
    &lt;address street="123 west st" city="seattle" state="wa" country="usa"/&gt;
    &lt;address street="321 south avenue" city="denver" state="co" country="usa"/&gt;
  &lt;/person&gt;
&lt;/people&gt;
</pre>
<p>You can now "query" the document with XPath expressions. Although walking the DOM tree can achieve similar results, using XPath expressions is much quicker and more powerful. If you can rely on <code>id</code> attributes, <code>document.getElementById()</code> is still powerful, but it's not nearly as powerful as XPath. Here are some examples.</p>
<pre class="brush: js">// display the last names of all people in the doc
var results = evaluateXPath(people, "//person/@last-name");
for (var i in results)
  alert("Person #" + i + " has the last name " + results[i].value);

// get the 2nd person node
results = evaluateXPath(people, "/people/person[2]");

// get all the person nodes that have addresses in denver
results = evaluateXPath(people, "//person[address/@city='denver']");

// get all the addresses that have "south" in the street name
results = evaluateXPath(people,  "//address[contains(@street, 'south')]");
alert(results.length);
</pre>
<h3 id="docEvaluateArray" name="docEvaluateArray">docEvaluateArray</h3>
<p>The following is a simple utility function to get (ordered) XPath results into an array, regardless of whether there is a special need for namespace resolvers, etc. It avoids the more complex syntax of <code><a href="/en/DOM/document.evaluate" title="en/DOM/document.evaluate">document.evaluate()</a></code> for cases when it is not required as well as the need to use the special iterators on <code><a href="/en/XPathResult" title="en/XPathResult">XPathResult</a></code> (by returning an array instead).</p>
<pre class="brush: js">// Example usage:
// var els = docEvaluateArray('//a');
// alert(els[0].nodeName); // gives 'A' in HTML document with at least one link

function docEvaluateArray (expr, doc, context, resolver) {
	doc = doc ? doc : (context ? context.ownerDocument : document);
	resolver = resolver ? resolver : null;
	context = context ? context : doc; 
	
	var result = doc.evaluate(expr, context, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	var a = [];
	for(var i = 0; i &lt; result.snapshotLength; i++) {
		a[i] = result.snapshotItem(i);
	}
	return a;
}
</pre>
<h3 id="getXPathForElement" name="getXPathForElement">getXPathForElement</h3>
<p>The following function allows one to pass an element and an XML document to find a unique string XPath expression leading back to that element.</p>
<pre class="brush: js">function getXPathForElement(el, xml) {
	var xpath = '';
	var pos, tempitem2;
	
	while(el !== xml.documentElement) {		
		pos = 0;
		tempitem2 = el;
		while(tempitem2) {
			if (tempitem2.nodeType === 1 &amp;&amp; tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name
				pos += 1;
			}
			tempitem2 = tempitem2.previousSibling;
		}
		
		xpath = "*[name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath;

		el = el.parentNode;
	}
	xpath = '/*'+"[name()='"+xml.documentElement.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']"+'/'+xpath;
	xpath = xpath.replace(/\/$/, '');
	return xpath;
}</pre>
<h3 id="Resources" name="Resources">Resources</h3>
<ul> <li><a href="/en/XPath" title="en/XPath">XPath</a></li> <li><a class="external" href="http://www.w3schools.com/xpath/">XPath tutorial</a></li> <li><a class="external" href="http://forums.mozillazine.org/viewtopic.php?t=229106">Forum discussion on this topic</a></li>
</ul>
<h2 id="See_also" name="See_also">See also</h2>
<ul> <li><a href="/en/Introduction_to_using_XPath_in_JavaScript" title="en/Introduction_to_using_XPath_in_JavaScript">Introduction to using XPath in JavaScript</a></li>
</ul>
<p>{{ languages( { "fr": "fr/Utilisation_de_XPath", "ja": "ja/Using_XPath", "zh-cn": "cn/Using_XPath", "ko": "ko/Using_XPath" } ) }}</p>
Revert to this revision