XPath tester
Run an expression against an XML document in this tab. XPath 1.0 — the version your browser actually implements — with the namespace prefixes bound by you.
- SECURE
- NO UPLOADS
- NO SIGNUP
- BROWSER BASED
- FREE
- FOREVER.
Support us with a link or a share
Paste the document, or open a local file.
XPath 1.0 — 2.0 syntax is named as such when it appears.
XPath 1.0 has no default namespace. If the document declares one, an unprefixed name test matches nothing — bind a prefix here and write it into the expression. The prefix is yours to choose; only the URI has to match.
Paste an XML document and an XPath expression.
This XPath tester runs XPath 1.0, and says so before you wonder
Every browser ships an XPath engine. It is reachable as document.evaluate, it is
the same engine that powers a Selenium or Playwright locator, and it implements
XPath 1.0 — the 1999 recommendation, and nothing after it. That is the engine
this page hands your expression to. No XPath library is downloaded, because there is nothing
to download: the implementation is already sitting in the tab you are reading this in.
The version matters more than it sounds like it should. Several well-known online testers run
XPath 2.0 or 3.1 on a server, usually Saxon, and an expression that works there can fail in
the place you actually needed it — a browser, a scraper built on lxml in its default mode, or
an Android layout inspector. Going the other way is worse: an expression written against 1.0
is silently accepted by a 2.0 engine and can mean something slightly different. So the number
is printed next to the expression field rather than buried in a footnote, and when your text
contains a construct that only exists in a later version, the page names the construct instead
of leaving you with the browser's wording. There is no matches() in XPath 1.0,
no upper-case(), no if … then … else, no for $x in, no
sequences, and no except. There is translate(),
substring-before(), contains(), count() and a predicate,
and that is a surprisingly long way.
The namespace resolver is the whole job, not an extra
Most XML that anyone brings to an XPath tester carries a namespace. A SOAP envelope, an Atom
feed, an XLIFF file, a Maven POM, a sitemap, an SVG — every one of them declares
xmlns on its root, and every element inside is therefore in that namespace. XPath
1.0 has no concept of a default element namespace, so the obvious expression
//book/title asks for elements in no namespace, matches zero nodes, and
reports no error whatsoever. That silence is the single most common reason people conclude
their expression is wrong when it is their binding that is missing.
The fix is a resolver, and here it is a visible field rather than a hidden behavior. Write
one binding per line as prefix = uri, then use that prefix in the expression:
b = http://example.com/books makes //b:book/b:title work. The prefix
is entirely yours to pick. It does not have to be the prefix the document happens to spell,
because XML matches on the namespace URI and the prefix in the source is just a local
nickname. A button fills the field with the namespaces the document actually declares, so you
can start from the truth and edit from there. If the expression uses a prefix you have not
bound, the result is nothing at all, and the page says which prefix — rather than throwing a
browser error that names no prefix and no position.
Four result types, because XPath returns four things
An XPath 1.0 expression evaluates to a node-set, a string, a number or a boolean, and which
one you get is decided by the expression, not by a setting. //book is a node-set,
string(//title) is a string, count(//book) is a number and
//book/@id = "b1" is a boolean. This page asks the engine for whichever type the
expression produces and prints it, so a tool that only ever showed matched nodes cannot mislead
you about what your expression really returns. For a node-set it also shows what XPath itself
makes of that set as a string, a number and a boolean — the first node's text, that text read
as a number, and whether the set is non-empty. Those three conversions are the reason
//price > 10 is true as soon as one price exceeds ten, and seeing them written
out tends to end the argument faster than reading the specification does.
Errors: the document has a position, the expression does not
A malformed document is caught before anything is evaluated, and the browser's own XML parser reports where it stopped — a real line and column, worded identically in Chrome and Safari, shown here exactly as given. Fixing an unbalanced tag is a job for the XML formatter and validator, which validates first and refuses to transform a broken tree.
A broken expression is different, and the difference is worth stating plainly. Chrome throws a syntax error whose message repeats your whole expression and carries no offset. Safari throws one whose message is “The string did not match the expected pattern” and whose line and column properties were measured to be identical for every expression tested, because they describe this page's own JavaScript rather than your text. Neither browser tells you where the expression broke. So this page does not pretend to: it says the browser gave no position, quotes the engine word for word, and separately runs its own bracket, parenthesis and quote balance check whose position is labeled as a measurement made here. An invented line number is worse than none, because the next person believes it.
What this page does not do
It does not generate an expression for you. Pointing at a node and asking for a path is a genuinely different tool with a different main surface — a rendered, clickable tree — and it would belong on its own address rather than as a mode on this one. It also does not run XPath 2.0 or 3.1, which would mean shipping a full engine and reviewing its license first; when that happens it will be its own page with its own version printed on it, not a checkbox here. For patterns over plain text rather than paths over a tree, the regex tester answers the neighboring question, and for the other structured formats there is a JSON formatter and an HTML formatter and minifier.
Parsing and evaluation happen in this browser tab. The XML someone brings to an XPath tester is usually an API response, a build configuration or a customer export, so nothing is placed in the address bar, in browser storage, or in a shareable link — which also means there is no link to share, deliberately. The verification guide explains how to confirm that boundary yourself with the network panel open.
Questions
Which version of XPath does this run?
XPath 1.0, and only 1.0. The engine is document.evaluate, built into your browser, and no XPath library is downloaded. Everything XPath 2.0 and 3.0 added is a syntax error here: if-then-else, for-return, sequences written as (1, 2), except and intersect, and the functions matches(), replace(), tokenize(), upper-case(), lower-case(), ends-with(), distinct-values(), min(), max(), avg() and current-date(). That was measured in both Chrome and Safari rather than assumed, and when your expression contains one of them this page names it instead of leaving you with the browser wording.
Why does my expression find nothing when the XML has a namespace?
Because XPath 1.0 has no default element namespace. A document whose root carries xmlns="http://example.com/books" puts every element in that namespace, and an unprefixed name test such as //book/title asks for elements in no namespace at all — so it matches zero nodes and reports no error. Bind a prefix in the namespace field, then write //b:book/b:title. The prefix is yours to choose; it does not have to be the one the document happens to use, because the document is matched on namespace URI and not on the prefix spelled in its source.
What are the four result types?
An XPath 1.0 expression returns a node-set, a string, a number or a boolean, and which one you get is a property of the expression rather than a setting. //book is a node-set, string(//title) is a string, count(//book) is a number and //book[1]/@id = "b1" is a boolean. This page asks the engine for ANY_TYPE, prints the type it chose, and for a node-set also shows what XPath itself would make of that node-set as a string, a number and a boolean — which is the step that explains why //price > 10 is true when any single price exceeds ten.
Why does a broken expression not show the character it broke on?
Because the browser does not report one. Chrome throws a SyntaxError whose message repeats the whole expression and carries no line, column or offset; Safari throws a SyntaxError with line and column properties that were measured to be identical for every expression tested, because they point at the JavaScript call and not at your text. Inventing a position would be worse than having none, so this page says plainly that the browser gave none. It does run its own bracket, parenthesis and quote balance check, and any position from that check is labeled as this page measuring, not as the browser reporting.
Does an invalid XML document report its position?
Yes, and that is a real difference worth knowing. The XML parser inside the browser does report where it stopped — for example, error on line 1 at column 11: Opening and ending tag mismatch — and both Chrome and Safari produce the same wording from the same parser. That position is shown exactly as given. No expression is evaluated until the document parses, because an expression run against half a tree answers a question you did not ask.
Is the XML uploaded anywhere?
No. Parsing and evaluation are calls into the XML parser and XPath engine already inside your browser, and there is nowhere for them to send anything. Nothing is written to the address bar or to browser storage either, which is deliberate: the XML someone brings to an XPath tester is usually an API response, a build configuration or a data export, and a shareable link would put it in your history and in whatever you pasted it into.