JSONPath Tester
Evaluate a JSONPath expression against your JSON and see every match highlighted in the source — RFC 9535 semantics, entirely on this device.
Matches appear here once you provide JSON and a JSONPath expression.
Your JSON appears here with every match highlighted once a query matches.
Press ? for shortcuts
Do it offline:$ jsonpath_ng '$.store.book[*].author' data.json
How to use it
- Paste or drop your JSON into the document pane; it is parsed on this device and never uploaded.
- Type a JSONPath expression in the query field — evaluation is live, with no Run button to press.
- Read the nodelist: every match in RFC 9535 document order, with the matched span highlighted back in the source.
- Copy the matches as JSON, download them, or share the document and query together as a link.
Paste JSON and a JSONPath expression; the matching nodes appear as a live nodelist, each highlighted in the source. This tester follows RFC 9535 — the 2024 standard — so wildcards, descendants, slices, unions and filters behave exactly as the spec defines. An empty result is shown as a valid outcome, not an error, and nothing you paste is uploaded.
How it works
A JSONPath expression starts at the document root, written $, and walks down through segments. Over the bookstore document, whose shape is $.store.book (an array) beside $.store.bicycle, a name segment like $.store.book selects a child by key, the wildcard $.store.book[*] takes every array element, the descendant operator .. in $..author reaches an author key at any depth, the slice $..book[0:2] takes the first two elements, and a union such as [0,2] lists several selectors in one bracket. Every match keeps its position, so the nodelist is always in document order.
A filter, written ?, keeps only the elements or members for which a test holds. Inside the test @ is the current node and $ is the whole document, so $.store.book[?@.price < 10] reads each book's own price while ?@.price < $.limit would compare it to a value elsewhere. Comparisons use ==, !=, <, <=, > and >=; naming a field on its own, as in ?@.isbn, is an existence test that holds when the field is present. Tests combine with &&, || and !, grouped by parentheses. Only a single-valued path — plain names and indices — may sit beside a comparison; a wildcard or descendant there is rejected, since it could stand for many values at once.
This tester implements RFC 9535, the JSONPath standard finalized in February 2024, rather than the informal 2007 blog syntax that older libraries copied, and two consequences follow. Constructs that predate the standard are refused by name rather than quietly ignored: a script expression such as (@.length-1), the =~ regular-expression operator and the ^ parent step were never part of RFC 9535, so the tester says so and points at the character that stopped it. The standard's function extensions — length(), count(), match(), search() and value() — are recognised and reported as not yet evaluated here, so a query that cannot run tells you why instead of returning a misleading empty result.
Examples
$.store.book[*].author$.store.book[0].author → "Nigel Rees"
$.store.book[1].author → "Evelyn Waugh"
$.store.book[2].author → "Herman Melville"
$.store.book[3].author → "J. R. R. Tolkien"The wildcard [*] takes every book in the array, and .author reads one field from each. The left column is each match's own normalized path; the right column is the value, copied byte-for-byte from the source.
$..author$.store.book[0].author → "Nigel Rees"
$.store.book[1].author → "Evelyn Waugh"
$.store.book[2].author → "Herman Melville"
$.store.book[3].author → "J. R. R. Tolkien"The descendant operator .. searches every level, so $..author finds the same four authors without naming store or book. Use it when you know the key but not its depth.
$.store.book[?@.price < 10].title$.store.book[0].title → "Sayings of the Century"
$.store.book[2].title → "Moby Dick"The filter keeps the two books under 10, and .title then reads a field from each survivor. @ is the book being tested; the comparison reads its own price.
$..book[0:2]$.store.book[0] → {"category":"reference","author":"Nigel Rees","title":"Sayings of the Century","price":8.95}
$.store.book[1] → {"category":"fiction","author":"Evelyn Waugh","title":"Sword of Honour","price":12.99}A slice [0:2] takes indices 0 and 1 — the end is exclusive — and each match is the whole book object, not just a field. Combine it with a trailing segment like .title to narrow the result.
$..book[?@.isbn]$.store.book[2] → {"category":"fiction","author":"Herman Melville","title":"Moby Dick","isbn":"0-553-21311-3","price":8.99}
$.store.book[3] → {"category":"fiction","author":"J. R. R. Tolkien","title":"The Lord of the Rings","isbn":"0-395-19395-8","price":22.99}Naming @.isbn with no comparison is an existence test: it keeps the two books that carry an isbn and drops the two that do not. The other fields on each kept book are untouched.
$.store.book[?@.price > 100]This query is valid and matched no nodes.No book costs more than 100, so the filter excludes every one and the nodelist is empty. That is a valid answer about the data, reported as a plain note rather than an error.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
An empty nodelist is a result, not an error
A query that parses but selects nothing returns zero nodes, shown as a plain note that it matched nothing. That is how you confirm a fact about the data — that no record meets a condition, say — separate from an expression that could not be parsed at all.
Single and double quotes are interchangeable in a name selector
$['title'] and $["title"] select the same member, so the quote style is your choice. Bracket-and-quote form is also the only way to name a key that dot notation cannot spell — one holding a space, a dot, or a leading digit.
Negative indices and steps count from the end
[-1] is the last element and [-2] the one before it. A slice step may be negative too: [::-1] walks the array in reverse, and [4:1:-1] steps backward from index 4 down to, but not including, index 1.
A descendant search can match a great many nodes
$..* visits every node in the document, so on a large payload the nodelist is capped at 10,000 matches. When the cap is reached the tool says so with an honest note; the count is never quietly trimmed to look complete.
A union keeps duplicates and order
Selectors in one bracket are applied left to right and their results concatenated, so $[0,0] returns the first element twice. Nothing is de-duplicated — repetition in the query is repetition in the nodelist, which is what lets a union repeat or reorder on purpose.
A comparison operand must be a single value
@.price < 10 is valid because @.price is one value, but @.book[*] < 10 is a syntax error: the wildcard could stand for many values, and RFC 9535 leaves no meaning for comparing a list. Existence tests have no such limit, so ?@.book[*] on its own is fine.
Common errors
A JSONPath expression must start with the root identifier `$`.- Every query begins at the document root. A path like store.book is missing that anchor; write $.store.book so the evaluator knows where to start reading.
Unexpected '2' at position 14 — a member name must start with a letter, `_`, or a non-ASCII character.- Dot notation names object members, so .2 is read as a member called 2, which is not allowed. To reach an array element by position use a bracketed index, $.store.book[2]; the caret sits under the exact character that stopped the parse.
length() is a function extension this tester does not evaluate yet.- RFC 9535 defines length(), count(), match(), search() and value(), but this tester does not evaluate them yet, so it names the one you used instead of returning a misleading empty result. Filter on a value you can reach directly, such as ?@.price < 10, until they land.
Unexpected '}' at line 1, column 22 — expected a value.- The JSON document itself is malformed — here an array opens and then closes with } before any value. The parser's own verdict is shown verbatim, with the line and column, so you can repair the source before the expression runs.
This JSON is larger than the 1 MB limit for this tool. For a bigger document, query it from the CLI: jsonpath_ng '$.store.book[*].author' data.json- Evaluation runs entirely in this tab, so the document is capped at 1 MB. Past that the tool declines the whole input rather than evaluate part of it, and points to a command-line tool that carries no such limit.
FAQ
Which JSONPath dialect does this tester follow?
It implements RFC 9535, the JSONPath standard published in February 2024. Where older articles and libraries disagree on a corner case, the behaviour here is whatever the RFC specifies, so an expression that works on this page carries the same meaning in any other conforming implementation.
Why does an expression like $.store.book[(@.length-1)] fail?
That parenthesised form is a script expression from the original 2007 Goessner article, and RFC 9535 dropped it. The standard has no way to run arbitrary code inside a query, so the tester reports it as unsupported rather than guessing. To reach the last element, use the negative index [-1].
Is an empty result the same as an error?
No. An expression that parses but selects nothing returns an empty nodelist, which the tester shows as a distinct, successful state. An error means the expression itself could not be parsed; an empty nodelist means it ran and the document simply held no matching node.
Does the JSON I paste get uploaded anywhere?
No. Parsing, evaluation and highlighting all run in this browser tab, and a document dropped onto the page is read locally the same way. Payloads often carry access tokens or personal records, so keeping the whole exchange on your device is the safe default.
How is this different from the JSON Path Finder and the jq Playground?
The path finder is for clicking a value in a tree to copy the path that leads to it; this tester goes the other way, taking a path expression and returning every node it selects. The jq playground runs a different language that reshapes and rebuilds JSON, whereas a JSONPath query only locates nodes that already exist.
Can a filter test more than one condition at once?
Yes. Filters combine comparisons with &&, || and !, and parentheses group them, so ?@.price < 10 && @.category == 'fiction' keeps only the cheap fiction. Each comparison reads one value from the current node, written @, against a literal or one other single value.
Do single and double quotes both work for a key name?
Yes. RFC 9535 accepts ['name'] and ["name"] as the same name selector, so the quote style is yours to pick. The bracket form is what you need for keys that dot notation cannot spell, such as names with spaces, dots, or a leading digit.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Evaluate the current expression again.
- ⌘/Ctrl + S
- Download the matched nodes as a .json file.
- ⌘/Ctrl + C
- With nothing selected, copy the matches as JSON.
- ⌘/Ctrl + Shift + C
- Copy the matches as JSON even with a selection active.
- t
- Switch to the next colour theme.
- ? or ⌘/Ctrl + /
- Open the keyboard-shortcut help.
- Esc
- Close the open overlay.