XPath 1.0 vs 2.0 vs 3.0 vs 3.1: What Your Tools Actually Support
Four XPath versions exist. Browsers, Selenium, Playwright, Appium and lxml all speak the first one from 1999. Here is what each version added, which processors implement it, and how to tell which you have.
The short version
| Version | Published | Where you get it |
|---|---|---|
| XPath 1.0 | November 1999 | every browser, Selenium, Playwright, Cypress, Puppeteer, Appium, lxml, libxml2, .NET System.Xml.XPath, Java javax.xml.xpath (default), HtmlAgilityPack |
| XPath 2.0 | January 2007 | XSLT 2.0 processors, Saxon, XQuery 1.0 engines |
| XPath 3.0 | April 2014 | Saxon 9.6+, BaseX, eXist-db, XQuery 3.0 engines |
| XPath 3.1 | March 2017 | Saxon 9.8+, BaseX, eXist-db, SaxonJS, XQuery 3.1 engines |
If you write locators for test automation or scrape with lxml, you are using XPath 1.0 and nothing on this page below the 1.0 section applies to you except as a warning about what will not work.
How to tell which version you have
Evaluate lower-case('A'). If it returns a, you have 2.0 or later. If it throws an invalid-expression error, you have 1.0. In a browser console:
$x("lower-case('A')") // SyntaxError in every browser: XPath 1.0
XPath 1.0 (1999)
The data model is four types: node-set, string, number, boolean. There are 27 functions. Everything on the functions reference page is the complete list.
Key properties that trip people coming from newer versions:
- Node-sets are unordered sets with document order applied on output. There are no sequences and no way to keep duplicates.
- Comparisons against node-sets are existential:
@x != 'a'means “some x is not a”. - No regular expressions, no
ends-with, no case functions, no date functions, no conditional expressions. - Only
|for combining node-sets. No intersect or except. - Numbers are IEEE doubles. There are no integers, so
1 div 3is0.3333....
Browsers implement it through document.evaluate (DOM Level 3 XPath). Firefox and Chromium each have their own engine; they agree on the spec closely enough that cross-browser XPath differences are rare.
XPath 2.0 (2007)
A different language that happens to be mostly backward compatible. The changes:
- Sequences replace node-sets. A sequence is ordered, can contain duplicates, and can hold atomic values (strings, numbers, dates) as well as nodes.
- XML Schema types:
xs:integer,xs:date,xs:dateTime,xs:duration, with arithmetic on dates and durations. - Expressions:
if (...) then ... else ...,for $x in ... return ...,some $x in ... satisfies ...,every .... - Regex functions:
matches(),replace(),tokenize(). - String functions:
lower-case(),upper-case(),ends-with(),string-join(),codepoints-to-string(),normalize-unicode(),compare(). - Sequence functions:
distinct-values(),index-of(),reverse(),subsequence(),exists(),empty(),min(),max(),avg(). - Set operators:
union,intersect,except. - Value comparisons:
eq,ne,lt,le,gt,gecompare single values and error on sequences, unlike the existential=.
Where it lives: XSLT 2.0 and XQuery 1.0. Saxon was, and remains, the reference implementation.
XPath 3.0 (2014)
Functions become first-class:
- Inline functions:
function($x) { $x * 2 }. - Function references and dynamic calls:
let $f := upper-case#1 return $f('a'). - Higher-order functions:
for-each(),filter(),fold-left(),fold-right(). - Simple map operator
!://item ! normalize-space(.)applies the right side to each item. - String concatenation operator
||. letexpressions outside XQuery.fn:parse-xml(),fn:serialize(),fn:unparsed-text().
XPath 3.1 (2017)
Adds the pieces needed to work with JSON:
- Maps
map { 'a': 1 }and arrays[1, 2, 3]as data types. - Lookup operator
?:$m?a,$arr?1,$json?items?*. - Arrow operator
=>:'a b' => tokenize(' ') => count(). - JSON functions:
parse-json(),json-doc(),json-to-xml(),xml-to-json(). - Map and array function libraries:
map:keys,map:merge,array:size,array:for-each, and so on.
3.1 is the current W3C Recommendation and the version most XML databases and XSLT 3.0 processors implement. SaxonJS brings it to browsers and Node, but as a separate library, not through document.evaluate.
Will browsers ever move past 1.0?
No sign of it. The DOM XPath API has not changed since 2004, no browser vendor has proposed updating it, and the Web Platform has moved toward CSS selectors and the Accessibility Object Model for element queries. XPath 1.0 in the browser is stable, complete, and effectively frozen. Every test tool inherits that.
For what is happening in the XPath language itself beyond 3.1, see XPath 4.0: the community draft.
Practical consequences for automation
- Learn the 27 functions and the
translate()idioms. That is the whole toolkit. - Do not copy examples from XSLT or XQuery tutorials without checking the function exists in 1.0.
- If you build a locator library, validate it against a browser, not against Saxon or an online XPath tester that defaults to 3.1. Several popular online testers do.
- When you need regex, do it in your test language after extracting text, or move the logic into an
executeScriptcall.
Related reading
- XPath 1.0 Functions Reference
- Case-Insensitive Matching
- XPath for Web Scraping, which covers the EXSLT regex extension available in lxml