JSONPath Evaluator
Run JSONPath queries against a JSON document and see the matched values. Supports child, index, slice, wildcard and recursive-descent operators.
Reviewed by Aygul Dovletova · Last reviewed
How to use the JSONPath Evaluator
- Paste the JSON document into the left input area. The parser is strict RFC 8259, so trailing commas, single quotes, JS-style comments, or stray BOM bytes surface as parse errors before the path even runs.
- Type the path expression in the second field starting with
$. Mix dot notation ($.store.book[0].title) and bracket notation ($['store']['book'][0]['title']) freely; use integer indexes, slice ranges, wildcards, and recursive descent as the structure demands. - Press Evaluate to walk the tree. Matched values are emitted as a JSON array in document order, even when the path resolves to a single value.
- Copy the result panel into your test fixture, your fetch script, or another tab. The full input and output stay client-side; nothing is uploaded to ZeroUtil or anywhere else.
What the evaluator does under the hood
The path string is tokenized into a small ordered stream of opcodes: root (the leading $), child for dot or quoted-bracket access, index for integer subscripts (with negative indexing for end-relative offsets), slice for end-exclusive Python-style ranges ([start:end] and [start:end:step]), wildcard for *, and recursive for the .. double-dot operator. The walker carries a working set of nodes through each opcode, growing or shrinking the set as the operators apply. The whole pipeline is a single pure function in JsonPathEvaluator.logic.ts with unit-test coverage, no eval calls, and zero runtime dependencies.
Recursive descent ($..name) is the most expensive operator because it visits every node beneath the current frontier. The implementation does a depth-first pre-order traversal and emits matches in document order, which is what every other JSONPath library does and matches the de-facto Goessner reference behavior. Filter expressions like [?(@.price < 10)] and script expressions like [(@.length-1)] are deliberately not supported because they require a sandboxed expression evaluator and bring questions about predicate safety on hostile input.
A worked example, operator by operator
Take the canonical Goessner bookstore document and watch how each operator narrows or widens the working set:
{
"store": {
"book": [
{ "title": "Sayings of the Century", "price": 8.95 },
{ "title": "Moby Dick", "price": 8.99 },
{ "title": "The Lord of the Rings", "price": 22.99 }
],
"bicycle": { "color": "red", "price": 19.95 }
}
}
$.store.book[0].titlewalks root, then thestorechild, then index 0 of thebookarray, then itstitle. Result:["Sayings of the Century"].$.store.book[*].priceuses a wildcard to fan out across every book, returning[8.95, 8.99, 22.99]in document order.$.store.book[-1].titletakes the last book by negative index:["The Lord of the Rings"].$.store.book[0:2].titleslices the first two books (end-exclusive):["Sayings of the Century", "Moby Dick"].$..pricedescends recursively, collecting everypricebelow root including the bicycle:[8.95, 8.99, 22.99, 19.95].
The last query is the one that catches people out. Recursive descent visits the whole subtree, not just the first match, which is why it is right for "find every id regardless of nesting" and wrong when you meant a single node.
When this tool earns its keep
- Pulling a specific value out of a deeply nested webhook payload (Stripe events, Shopify orders, GitHub action contexts) without writing a throwaway JS one-liner.
- Building snapshot tests where you want to assert "every
idin this OpenAPI document is a string", drafted as$..idfirst. - Explaining to a teammate why their
$.users[*].emailquery returns an empty array because the actual key isusersat depth 2, not depth 1. - Extracting a list of file paths from a Cloudflare Pages build manifest, a Vercel deployment response, or a Kubernetes
kubectl -o jsondump. - Sketching the JSONPath you intend to put into a Datadog monitor, a Logz.io alert, or a Postman test before committing the configuration.
- Validating that a long-tail JSON document actually contains the field you expect at the depth you assumed, without instrumenting code.
Common pitfalls and edge cases
- Recursive descent is greedy.
$..priceon a document with bothstore.bicycle.priceandstore.book[*].pricereturns every match, not the first. If you want only the books, anchor with$.store.book[*].price. - Slices are end-exclusive.
[0:2]returns elements 0 and 1, not 0 through 2 inclusive. This matches Python and the JSONPath reference but burns engineers used to inclusive ranges. - Negative indexes wrap silently.
[-1]is the last element,[-100]on a 5-element array is no match (not an error). Plan for both behaviors when generating paths programmatically. - Dot notation cannot reach keys with dots in them. A key literally named
"file.json"needs bracket notation:$['files']['file.json']. Dot syntax would parse it as a nested path. - Wildcards on objects iterate values, not keys.
$.headers.*returns the values of every header, in insertion order. There is no key-introspection operator in the JSONPath grammar. - Filter expressions are not supported. If your path contains
?(...), switch to a richer engine likejsonpath-plus, JMESPath, or jq. The omission is intentional.
JSONPath origins and the IETF RFC 9535
JSONPath was sketched by Stefan Goessner in 2007 as a JSON analog to XPath. The reference implementation was a snippet of JavaScript that pages copied for years without a formal specification. In 2024 the IETF published RFC 9535, the first standards-track JSONPath document, which fixes corner cases (Unicode normalization, whitespace handling, slice semantics) and defines a portable test suite. The grammar implemented here is the common subset both Goessner and RFC 9535 agree on; richer engines such as Burke Library's jsonpath-plus, Java's Jayway, and Python's jsonpath-ng add filter and script expressions on top.
Alternatives and when they beat this tool
Command-line jq is the right choice for transformations rather than extractions; it has a richer query language, supports streaming, and runs on gigabytes of data without the browser memory pressure. JMESPath (the AWS CLI's --query flag) is a different but related grammar with stronger filter and projection support; if you live in aws, az, or gh CLIs, prefer it. Inside Node, jsonpath-plus on npm covers the filter expressions this evaluator omits. JSON Pointer (RFC 6901) is the right choice when you only need a single, fully-specified path with no wildcards. Use this evaluator when you want to draft and verify a path interactively in the browser, do not want to install or learn another query language, and do not want to paste sensitive payloads into a remote service like jsonpath.com.
Frequently Asked Questions
Which JSONPath syntax does this tool implement?
The common subset most engineers reach for: <code>$</code> root, <code>.child</code>, <code>['child']</code>, <code>[N]</code>, <code>[-N]</code>, <code>[*]</code>, <code>[start:end]</code> end-exclusive slice, and <code>..name</code> recursive descent. Filter expressions <code>[?(@.foo)]</code> and script expressions are deliberately omitted - a richer engine like jsonpath-plus is the right pick if you need them.
Why are filter expressions not supported?
Filter expressions add a JavaScript-like predicate engine and the attendant safety questions about untrusted input. The evaluator here is a pure walker over the document tree, which keeps the scope small and the attack surface zero. For predicate filtering, pipe the matched array through a separate JS expression on your side, or use jsonpath-plus.
Does the evaluator upload my JSON anywhere?
No. The whole evaluation runs inside a Preact component in your browser. There is no fetch call or analytics beacon for the document or the query. Open the Network tab in DevTools to confirm.
How does recursive descent ($..name) behave?
It collects every value whose key is exactly <code>name</code> anywhere below the current node, in document order. <code>$..price</code> on a bookstore document returns the price of every book and the bicycle, in the order they appear in the source.
How is index -1 handled on an empty array?
The result set is empty. Negative indices wrap from the end: <code>[-1]</code> is the last element, <code>[-2]</code> is the second-to-last. If the resulting offset falls outside the array, the evaluator emits no match rather than throwing.
What is the difference between dot and bracket notation?
They address the same nodes. <code>$.store.book</code> and <code>$['store']['book']</code> are equivalent. Bracket notation is mandatory when a key contains a dot, a space, or other punctuation, for example <code>$['user-agent']</code> or <code>$['file.json']</code>. Dot notation is terser for plain identifier keys, so most engineers mix the two in one path.
Does an empty result mean my path is wrong?
Not necessarily. An empty array is a valid, correct result when nothing matches - for instance <code>$.users[*].email</code> on a document with no users. The most common cause of an unexpected empty result is an off-by-one in the nesting depth or a key typo, so verify the document structure with <code>$.*</code> or <code>$..*</code> to see what is addressable.
How do slices with a step work?
<code>[start:end:step]</code> mirrors Python. <code>[0:6:2]</code> takes indices 0, 2, and 4 from the slice window; <code>end</code> stays exclusive. Omitting a component uses the default - <code>[::2]</code> is every other element from the start, <code>[1:]</code> is everything from index 1 onward.
Can I run the same query against many documents?
The evaluator works one document at a time. To batch over a directory, confirm the path here, then run it with <code>jq</code> or a Node script using <code>jsonpath-plus</code>. The tool is best for getting the path right, not high-volume extraction.
Does the tool show me which node each match came from?
The output is the array of matched values in document order. If you need the location of each match rather than its value, use a JSON Pointer engine or the <a href="/tools/json-diff-viewer/">JSON Diff Viewer</a>, which reports structural position.
More Developer Tools
Base64 Encoder & Decoder
Encode UTF-8 text to Base64 online or decode Base64 back to UTF-8 and plain text. Runs in your browser with no upload.
Open toolBulk URL Encode / Decode
Encode or decode many URLs at once. Paste a newline-separated list and the tool processes each line in parallel, preserving order and blank lines.
Open toolCode Screenshot
Create beautiful code snippet images with customizable themes.
Open toolColor Converter
Convert colors between HEX, RGB, HSL and CMYK formats.
Open toolCron Expression Parser
Parse cron expressions into human-readable schedules with next run times.
Open toolCSS Formatter / Minifier
Format, beautify and minify CSS code.
Open tool