JSONPath Cheat Sheet with Examples
· 4 min read
JSONPath is a query language for selecting parts of a JSON document: $ is the root, .key or ['key'] selects a child, [*] selects all elements, .. searches at any depth, and [?@.price < 10] filters by a condition. It was standardized as RFC 9535 in 2024; try every example below in the JSONPath tester.
Sample document
All examples use this document:
{
"store": {
"books": [
{ "title": "Dune", "author": "Frank Herbert", "price": 9.99, "tags": ["sci-fi"] },
{ "title": "Emma", "author": "Jane Austen", "price": 4.5, "tags": ["classic", "romance"] },
{ "title": "Neuromancer", "author": "William Gibson", "price": 12, "isbn": "0-441-56956-0" }
],
"bicycle": { "color": "red", "price": 199 }
}
}Syntax reference
| Expression | Meaning | Result on the sample |
|---|---|---|
$ | The root node | The whole document |
$.store.bicycle.color | Child by name (dot notation) | "red" |
$['store']['bicycle'] | Child by name (bracket notation) | The bicycle object |
$.store.books[0].title | Array index (zero-based) | "Dune" |
$.store.books[-1].title | Negative index counts from the end | "Neuromancer" |
$.store.books[*].author | Wildcard: every element | All three authors |
$.store.* | Wildcard: every member of an object | The books array and the bicycle object |
$..price | Recursive descent: price at any depth | 9.99, 4.5, 12, 199 |
$.store.books[0,2].title | Multiple indexes (union) | "Dune", "Neuromancer" |
$.store.books[0:2].title | Slice: start inclusive, end exclusive | "Dune", "Emma" |
$.store.books[::2].title | Slice with step | "Dune", "Neuromancer" |
$.store.books[?@.isbn].title | Filter: has the member | "Neuromancer" |
$.store.books[?@.price < 10].title | Filter: comparison | "Dune", "Emma" |
$..books[?@.author == 'Jane Austen'].title | Filter: equality | "Emma" |
A JSONPath query always returns a list of matches (a "nodelist"), even when it matches one value or none. Tools differ in whether they unwrap single results.
Filter expressions
Inside [? ], @ is the current element being tested. You can combine conditions with &&, || and !, and group with parentheses.
$.store.books[?@.price >= 5 && @.price <= 10].title -> "Dune"
$.store.books[?@.tags && !@.isbn].title -> "Dune", "Emma"
$.store.books[?@.author == 'Frank Herbert' || @.price > 10].title
-> "Dune", "Neuromancer"Older implementations based on Stefan Goessner's original 2007 article write filters with parentheses: $..books[?(@.price < 10)]. RFC 9535 accepts the parenthesized form too, so it is the most portable way to write a filter.
Functions (RFC 9535)
| Function | Example | Matches |
|---|---|---|
length() | $.store.books[?length(@.tags) > 1].title | "Emma" |
count() | $.store[?count(@.*) > 2] | Members of store with more than two children |
match() | $.store.books[?match(@.title, 'D.*')].title | "Dune" (whole string must match) |
search() | $.store.books[?search(@.author, 'Austen')].title | "Emma" (substring match) |
value() | $.store.books[?value(@..price) > 10].title | Converts a single-node result to a value for comparison |
match and search use the I-Regexp dialect (RFC 9485), a portable subset of regular expressions. Older libraries often support =~ instead, with their own regex flavor.
Common recipes
| Task | Expression |
|---|---|
| All values of a key anywhere | $..id |
First item of every list named items | $..items[0] |
| Objects where a flag is true | $.users[?@.active == true] |
| Items missing a field | $.users[?!@.email] |
| Last N items | $.events[-3:] |
| A key with a dash or space | $['content-type'] or $.headers['x-request-id'] |
Gotchas
- Implementations differ. Before RFC 9535 there was no standard, so libraries disagree on details like filter syntax, regex support and how results are returned. Check which version your library implements.
- String quotes. RFC 9535 accepts single or double quotes inside expressions. Single quotes are easier when the query itself is embedded in a JSON string or a shell command.
- Recursive descent is expensive on large documents because it visits every node. Prefer an explicit path when you know it.
- Keys with dots.
$.a.bmeans nested keys. For a key literally nameda.b, use$['a.b']. - No parent selector or transformation in the standard. JSONPath selects; it does not reshape output. For that, use jq (see jq vs JSONPath).
Finding the path to a value
The quickest way to write a query on an unfamiliar document is to find the value in a tree view and copy its path. The JSON viewer lets you click any node and copy a path you can paste into the JSONPath tester, then generalize it with wildcards or filters.
FAQ
What does $ mean in JSONPath?
$ is the root of the document. Every query starts from it, for example $.store.books.
What is the difference between . and .. in JSONPath?
. selects a direct child. .. (recursive descent) selects matching members at any depth below the current node.
How do I filter an array in JSONPath?
Use a filter selector: $.items[?@.price < 10]. @ is the current element. Older libraries need parentheses: [?(@.price < 10)].
Is JSONPath standardized?
Yes. RFC 9535, published by the IETF in February 2024, defines JSONPath. Many existing libraries predate it and behave slightly differently.
Is JSONPath the same as JSON Pointer?
No. JSON Pointer (RFC 6901), such as /store/books/0/title, identifies exactly one location and has no wildcards or filters. JSONPath is a query language that can return many matches.

