Skip to content
ToolBoxGeniehome

JSONPath Tester

Developer Tools · Added

Write a JSONPath expression, see what it selects, and get the normalised path of every match — which is the part you paste into the code that has to fetch it for real. It implements the subset that behaves identically across implementations and RFC 9535, and refuses anything outside that with a message rather than quietly returning nothing.

$ is the root. Supports . [n] [start:end] * .. and [?(@.field > value)].

Try one

3 matches

Matches
3
Nodes examined
9

A recursive descent walks the whole document

Result shape
list
PathValue
$['store']['book'][0]['title']Sayings of the Century
$['store']['book'][1]['title']Sword of Honour
$['store']['book'][2]['title']Moby Dick

JSONPath was a blog post in 2007 and only became a specification — RFC 9535 — in 2024, so implementations still disagree at the edges. This supports the subset that behaves the same everywhere: child access, indexes including negative ones, slices, the wildcard, recursive descent, and single-comparison filters. Script expressions and functions are refused with a message rather than guessed at, because a query that works here and nowhere else would be worse than none. Everything runs in this tab; the document is never uploaded.

How to use the jsonpath tester

  1. 1Paste your JSON, or use the sample document.
  2. 2Write a path, or press one of the examples to see a feature.
  3. 3Read the matches — each row shows the value and the exact path to it.
  4. 4Copy the values as JSON, or the paths for your code.

Examples

Every value at any depth

Input
$..price
Result
Four matches, each with its own path: three book prices and the bicycle's

A filter

Input
$.store.book[?(@.price < 10)]
Result
The two books under ten, as whole objects with their array indexes

Something it will not guess at

Input
$.book[?(@.title.match(/^S/))]
Result
Refused, with the reason: script expressions are not portable between implementations

An empty result meaning “no matches” and one meaning “I did not understand” are different answers.

About the jsonpath tester

Why JSONPath has dialects at all

XPath had a specification before it had implementations. JSONPath had it the other way round: Stefan Gössner's 2007 article sketched a syntax, people implemented it in a dozen languages, and each resolved the ambiguities differently. Does a filter on a non-existent field match or skip? Does a negative index count from the end everywhere or only in arrays? Does $..* include the root? Different libraries answer differently, and all of them are defensible readings of the original.

RFC 9535 settled it in 2024, but the installed base did not vanish. So the practical advice for anything that has to run in more than one place is to stay inside the intersection — which is what this tool implements and why it declines to extend past it.

Recursive descent is expensive, and worth knowing when

$..price reads as a small query and is not. It walks every node in the document looking for the key, so its cost is the size of the whole document rather than the depth of the path. On a configuration file that is instant; on a large API response inside a loop it is the reason a script is slow.

The nodes-examined figure here is there for that reason. A path with an explicit prefix — $.store..price rather than $..price — narrows the search before the descent begins, and the difference in that number shows what it saved. Where the shape is known, spelling it out is faster and it also documents the assumption for whoever reads the code next.

When a query returns nothing

An empty result has two completely different causes, and a tool that renders both as a blank panel is unhelpful. Either the path was understood and matched nothing — a statement about your document — or it was not understood at all.

This separates them: a path it cannot parse produces an error naming the problem, and a path that parses and finds nothing says so explicitly. The usual causes of the second are a spelling difference, an array where an object was expected, or a filter comparing a string to a number. That last one is silent in most implementations: `[?(@.price > '10')]` compares a number to a string, and this refuses to order values of different types rather than inventing a result.

Frequently asked questions

Which JSONPath does this implement?
The portable subset. JSONPath started as a blog post in 2007 rather than a specification, so implementations diverged for seventeen years until RFC 9535 standardised it in 2024. Supported here: the root, child access by dot or bracket, indexes including negative ones, slices with an optional step, the wildcard, recursive descent, and filters comparing one field against one literal. Those behave the same in every implementation worth naming.
Why refuse script expressions instead of trying?
Because a query that works here and nowhere else is worse than no query. Script expressions, function calls and regular-expression matching are exactly where implementations disagree — some evaluate JavaScript, some have their own mini-language, the RFC has functions none of the older ones support. Guessing would hand you a path that fails the moment you paste it into your actual stack.
What is the difference between .. and *?
The wildcard selects every immediate child of the current node — one level. Recursive descent searches every level below it. So $.store.* gives you the book array and the bicycle object, while $..price finds every price anywhere, however deep. Recursive descent is much more expensive: it walks the entire subtree, which the match count and the nodes-examined figure both show.
Why show the path of each match rather than just the value?
Because the value alone rarely finishes the job. If a query returns three prices, the next question is always which items they belong to — and the normalised path answers it in a form you can paste straight into code. It also disambiguates: two matches with identical values are clearly different nodes when their paths differ.
Is my JSON uploaded anywhere?
No. The parser and the evaluator both run in this tab, so a production API response or a document with customer data in it stays on your machine. There is no request to this site or anywhere else while you are using it.