XPath 1.0, 2.0 and 3.1: what changed
The version you are allowed to use is decided by your engine, not your preferences, and the jump from 1.0 changes what a query can even express.
What XPath 1.0 cannot express
- No sequences: every expression returns one node-set, string, number or boolean, so returning a mix is impossible.
- No
for,if,someoreveryexpressions — iteration needs XSLT or host code. - No date or duration types, so date comparison is string comparison or a host-language function.
- No regular expressions:
matches(),replace()andtokenize()are all 2.0. - No user-defined functions, and no partial application of functions.
- One numeric type, a double, so integers and decimals are approximate.
<!-- Locating a book whose price is a two-decimal number: not possible in 1.0,
where every value is a string or a double and a trailing zero is lost -->Sequences, functions and types
<!-- XPath 2.0 and later -->
for $b in //book[price > 20]
return string-join(($b/title, $b/author), ' by ')
if (count(//book) gt 10) then 'long' else 'short'
//book[matches(title, '^The\s', 'i')]
//book[upper-case(@lang) eq 'EN']
distinct-values(//book/@lang)
xs:date(//book[1]/published) lt current-date()
//book[price castable as xs:decimal]| Feature | 1.0 | 2.0 | 3.1 |
|---|---|---|---|
| Sequences of mixed types | No | Yes | Yes |
| Regular expressions | No | Yes | Yes |
| for / if / quantified expressions | No | Yes | Yes |
| Date and duration types | No | Yes | Yes |
| Maps and arrays | No | No | Yes |
| Inline functions and higher order functions | No | No | Yes |
| Engines | Everywhere | Saxon, Xalan-J 2.7 (limited) | Saxon 9.7+ |
# Saxon gives you XPath 3.1 on the command line for a one-off query
java -cp saxon-he.jar net.sf.saxon.Query -qs:"count(//book[matches(title,'^The')])" catalog.xml
# xmllint and xmlstarlet remain XPath 1.0 and silently differ on edge cases💡
Version compatibility is not additive in practice: in a 2.0-only engine,
1 div 0 raises an error where 1.0 returned infinity, and comparing two node-sets that contain more than one node is an error rather than a set intersection. Porting means testing, not just changing a version number.FAQ
Which version does my engine speak?
Browsers, xmllint, xmlstarlet, Python's lxml, the JDK and the .NET BCL are all XPath 1.0. Saxon and BaseX give you 3.1. XSLT 3.0 processors bundle an XPath 3.1 engine.
Is it worth moving a working 1.0 query to 2.0?
Only for something 1.0 genuinely cannot do, such as date arithmetic, regex matching or returning a sequence. Rewriting a working expression risks subtler differences than the one you are fixing.
Related
XPath in Java and .NET XPath performance: cost, indexes and //
Last refreshed 2026-09-18.