JSONPath Tester
Run a JSONPath expression against a document and see every matching node with its normalized path — filters, wildcards and recursive descent included.
JSON document
Matches
Paste a JSON document to run the expression against it.
The document is parsed and queried in your browser; nothing is uploaded. Paths are reported in the normalized form defined by RFC 9535, which is the unambiguous way to address a single node.
About the JSONPath Tester
JSONPath is the query language you reach for when a document is bigger than the value you want out of it — a `$..price` instead of four nested loops. This JSONPath tester runs an expression against your own document and shows every node it selects, live, with the exact path to each match.
The implementation follows RFC 9535, which standardised JSONPath in 2024 after two decades of implementations quietly disagreeing with each other. Child and descendant segments, wildcards, index and slice selectors, unions and filter expressions all behave as the RFC specifies — including the parts that surprise people, such as a filter comparing a missing member being false rather than an error.
Each result carries its normalized path: `$['store']['book'][0]['title']`. That form is defined by the RFC as the single canonical way to address one node, and it is what you want when the query was exploratory and the code that follows has to point at one specific place.
- RFC 9535 semantics, including slice bounds, negative indexes and filter comparison rules
- Filters with ==, !=, <, <=, >, >=, &&, ||, !, existence tests and length()
- Both the standard `[[email protected] < 10]` syntax and the older `[?(@.price < 10)]` form
- Recursive descent `..`, wildcards, unions of names or indexes
- Normalized path for every match, ready to paste into code
- Syntax errors located with a caret, rather than reported as no matches
How to use it
- Paste your JSON document, or load the sample.
- Type a JSONPath expression in the box at the top. Results update as you type.
- Use the buttons underneath to drop in a common pattern and adapt it.
- Read the matches on the right, and the normalized path of each match below.
- Copy either the matched values or the list of paths.
Real-world use cases
Backend & API developers
Prove out a JSONPath expression against a real payload before it goes into an ETL step or a data pipeline, rather than debugging it from inside a script's stack trace.
QA & test engineers
Work out the exact path an assertion needs against a captured API response before writing it into a REST-assured, Postman or contract test.
DevOps & platform engineers
Draft the JSONPath expression for `kubectl get pods -o jsonpath=...` or a similar CLI flag against a saved sample first, where a mistake costs a full round trip to the cluster.
Frontend developers
Pull one field out of a deeply nested REST or GraphQL response while debugging, without writing throwaway `console.log` chains to get there.
Data engineers
Explore an unfamiliar webhook payload or log entry to find exactly where a value lives before writing code that depends on it.
Examples
Every value of a key, at any depth
$..price
[399, 8.95, 12.99, 8.99, 22.99]
`..` is recursive descent: it looks at the node and every descendant. It is the expression that makes JSONPath worth using, and the one that is easy to make accidentally expensive on a large document.
A filter over an array
$.store.book[[email protected] < 10].title
["Sayings of the Century", "Moby Dick"]
`@` is the element being tested. The filter selects elements; the `.title` after it then applies to each one that survived.
Existence, and its negation
$.store.book[[email protected]] $.store.book[[email protected]]
the two books that have an ISBN the two that do not
A bare path in a filter tests for existence. Note that a member present with the value `false` does not count as existing for this purpose — write `[[email protected] == false]` if that is what you mean.
Slices and negative indexes
$.store.book[0:2] $.store.book[-1]
the first two books the last book
Slices are `start:end:step` with an exclusive end, exactly like Python. A negative index counts from the end.
Common errors
| Message | Cause | Fix |
|---|---|---|
| A JSONPath query must start with $ | The expression begins at a property name — `store.book` rather than `$.store.book`. | Every query is rooted at `$`. Inside a filter, `@` refers to the current element and `$` still means the whole document. |
| Quote the property name: ['name'] | A bare word inside brackets. `$[name]` is not valid; brackets take an index, a slice, a filter or a quoted name. | Write `$['name']` or `$.name`. Bracket-quoted form is what you need for keys with dashes, dots or spaces. |
| Use == for comparison, not = | A single `=` in a filter, which is assignment in most languages and nothing at all in JSONPath. | Use `==`. The full set is `== != < <= > >=`. |
| The expression returns nothing | Usually a type mismatch in a comparison: `[[email protected] < '10']` compares a number with a string, and the RFC defines ordering only between two numbers or two strings. | Drop the quotes around numeric literals. Comparing across types is false rather than an error, which is why this fails silently. |
| match() is not supported by this tool | The RFC's optional regexp function extension, which needs an I-Regexp engine to be implemented correctly. | Filter on what you can express here, or use the regex tester on the extracted values. |
| A second selector does not index into the results | `$.book[::-1][0]` reads as "reverse, then take element 0 *of each book*" — selectors apply to every node they are given, not to the result list. | The order of the returned nodes carries the reversal. There is no way to index into a result list from within one expression. |
Frequently asked questions
›Is my document uploaded anywhere?
No. Parsing and evaluation both happen in your browser. The API response you are exploring never leaves the page, and nothing is stored or logged.
›Which JSONPath dialect does this implement?
RFC 9535, published in 2024. Before it, JSONPath was defined only by a 2007 blog post and every implementation diverged — which is why the same expression can behave differently in Jayway, Newtonsoft and jsonpath-ng. Where this tool differs from an older library, the RFC is the reason.
›What is a normalized path?
The canonical way to address exactly one node: bracket notation, single-quoted names, numeric indexes — `$['store']['book'][0]`. Any node has exactly one normalized path, which makes it safe to store, compare or paste into code, unlike a query that may match many nodes.
›How is JSONPath different from JSON Pointer?
JSON Pointer (RFC 6901) addresses one node by a literal path and has no querying at all — it is what JSON Patch and JSON Schema errors use. JSONPath is a query language: wildcards, recursive descent and filters, returning zero or many nodes. Use Pointer to say where something is; use JSONPath to find it.
›Why does a filter on a missing field not throw?
Because the RFC defines a comparison involving a path that selects nothing as simply false — with the deliberate exception of `!=`, where "a value that does not exist is not equal to 3" is the useful answer. This makes filters safe over heterogeneous arrays, at the cost of making a typo look like an empty result rather than an error.
›Does it work on very large documents?
It is bounded by your browser's memory, and results are capped at 10,000 nodes so a `$..*` on a huge payload cannot lock the page up. Recursive descent visits every node, so it is the expression to watch on multi-megabyte input.
›Does it support length() and count()?
Yes, both are implemented inside filters, and both measure the same thing here: the size of the single value their argument resolves to — a string's character count, an array's length, or an object's key count. `[?length(@.tags) > 2]` keeps items with more than two tags. Neither is supported outside a filter expression.
›Can I use a script expression like [(@.length-1)]?
No, and it is reported rather than silently mismatched. Script expressions are a separate, optional part of the RFC from filter expressions, and this tool implements the filter grammar — comparisons, boolean logic, existence tests, `length()`/`count()` — but not arithmetic inside a selector.
Last updated