JSONPath Query
JSONRun JSONPath / jq-style queries against a JSON document and highlight every match.
Remote URLs are not fetched; paste your JSON directly.
On this page
What is a JSONPath tester?#
When a JSON document is small, you read it with your eyes. When it is a few thousand lines — an API response, a config dump, a dependency tree — your eyes stop being the right tool. JSONPath is the query language for that situation: a compact expression that says “give me every price field”, or “the titles of the books cheaper than 10”, or “every node named isbn no matter how deep it is buried”.
Think of it as a cross between a file path and a tiny filter language. $.store.book[0].title walks down a known structure the way a filesystem path does. $..author says “descend recursively and collect every author you find”. [?(@.price < 10)] is a filter: keep only the elements where the current node’s price is under ten. The @ always means “the node I am currently looking at”.
This page runs a JSONPath expression against your JSON, lists every match, and — the part that saves the most time — highlights the matched ranges directly on the formatted document so you can see at a glance whether your expression selected what you think it selected. Filters are evaluated with a safe expression parser, never by executing code, so an untrusted blob cannot run anything.
How to use it#
- Paste your JSON into the Input pane on the left, or click Sample to load the classic bookstore fixture.
- Type a JSONPath expression into the JSONPath field on the right. Common starting points:
$.store.book[*].author— every array element’sauthor.$..author— recursive descent: everyauthor, anywhere.$.store.book[?(@.price < 10)].title— a filter expression.$.store.book[?(@.isbn)].isbn— an existence test (has anisbnkey).
- The matches list below the field fills with one entry per result, and the full-width JSON preview underneath highlights every matched span in place.
- The header next to Matches shows the count. An expression that matches zero nodes still reports success (empty list) — only a syntactically broken expression shows an error.
- Click Copy matches on the toolbar to grab the matched values as a JSON array, or Clear to reset both panes.
Queries fire as you type. If the JSON itself is invalid, the status bar points at the exact row and column to fix.
Key features#
- In-place highlighting. Every matched value is overlaid on the formatted document, so a recursive
$..that grabs thirty nodes is instantly visible instead of buried in a list. - Safe filter evaluation. Expressions like
[?(@.price < 10)]are parsed by an expression evaluator, not handed to dynamic code execution — the page stays CSP-safe even on hostile input. - Distinct “no match” vs “error”. A valid expression that selects nothing is reported as zero matches; only a malformed expression is flagged as an error. You never confuse “found nothing” with “broke”.
- Pointer-per-match. Each result carries its JSON Pointer (for example
/store/book/2/title), so you know exactly where in the tree a value came from. - Copy as an array. The matched values export as a clean JSON array, ready to pipe into the next step.
- Local only. Your data is queried in the browser. There is no backend and no upload.
Worked example#
Load Sample, then run a filter that finds the titles of cheap books. Enter this in the JSONPath field:
$.store.book[?(@.price < 10)].title
The matches list returns two titles, because only two of the four books in the fixture are priced under 10:
[
"Sayings of the Century",
"Moby Dick"
]
The JSON preview highlights those two title values in place, and you can see why the other two were excluded — Sword of Honour (12.99) and The Lord of the Rings (22.99) sit above the threshold. Swap to an existence test to find which books carry an ISBN:
$.store.book[?(@.isbn)].isbn
That returns the two ISBN strings and nothing else, because the filter keeps only elements that actually have an isbn key — a handy pattern for finding “which records are missing a field” by negating it.
FAQ#
What is the difference between $ and @?#
$ is the root of the document — the entire JSON value you pasted. @ is the current node being considered inside a filter. So in [?(@.price < 10)], the @ refers to whichever book the filter is testing right now; writing $.price there would jump back to the root and look for a top-level price that does not exist.
My expression matches nothing. Is that a bug?#
Probably not. A valid expression that selects zero nodes is a successful query with an empty result — for example, $.store.book[?(@.price > 1000)] on the sample simply means no book is that expensive. The header reads “0 matches” without an error. Only when the expression itself is unparseable does the page report an error.
Why does a filter not execute arbitrary JavaScript?#
Filters are evaluated by a dedicated expression parser that understands comparisons, boolean operators, and @-references — it never builds and runs code. This is deliberate: it keeps the tool safe to point at JSON you do not fully trust, and it keeps the page inside the browser’s content-security policy.
Can JSONPath edit or remove nodes?#
No — this is a read-only query tool. It selects and displays, it does not mutate your JSON. If you want to see how two documents differ instead, reach for the /en/json/diff/ tool.