DEV Community

Souvik Pramanik
Souvik Pramanik

Posted on Originally published at json-util.com

JSONPath Cheat Sheet: Syntax, Examples & Common Mistakes

JSONPath looks simple until you actually need a filter, a slice, or a recursive match - then the handful of symbols that looked obvious start behaving unpredictably. This is a complete reference: every operator with a working example, a real query built step by step, the mistakes that most often cause a silent empty result, and how JSONPath stacks up against JMESPath and XPath.

Full syntax reference

Syntax Meaning Example
$ Root of the document - every expression starts here $
.prop / ['prop'] Child property, by dot or bracket notation $.user.name
[*] Wildcard - all items in an array, or all properties of an object $.items[*]
.. Recursive descent - matches a property at any depth $..price
[0], [-1] Index into an array (some libraries support negative indices) $.items[0]
[0,2,5] Union - multiple indices or property names at once $.items[0,2,5]
[start:end:step] Array slice, end-exclusive, step optional $.items[1:5:2]
[?(<expr>)] Filter - keep only elements where <expr> is true $.items[?(@.price < 10)]
@ The current element being tested, used inside a filter @.inStock === true
=~ /regex/i Regex match inside a filter (library extension, not core spec) @.name =~ /^a/i
&& / `\ \ `

Worked example: querying a real API-shaped response

Say an API returns this order history payload:

{
  "customer": "Alex",
  "orders": [
    { "id": 101, "status": "shipped", "total": 42.50, "items": [{ "sku": "A1", "qty": 2 }] },
    { "id": 102, "status": "pending", "total": 15.00, "items": [{ "sku": "B4", "qty": 1 }] },
    { "id": 103, "status": "shipped", "total": 8.25,  "items": [{ "sku": "A1", "qty": 1 }] }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Build the query one requirement at a time:

  1. "Give me every order total." Walk from the root into the array and grab the field: $.orders[*].total[42.50, 15.00, 8.25]
  2. "Only shipped orders." Add a filter before drilling into the field: $.orders[?(@.status === 'shipped')] → the two matching order objects
  3. "Only shipped orders over $10." Combine two conditions with &&: $.orders[?(@.status === 'shipped' && @.total > 10)] → just order 101
  4. "Just the SKUs from those orders, wherever they appear." Recursive descent skips having to know the exact nesting depth: $.orders[?(@.status === 'shipped')]..sku["A1", "A1"]

That's the general pattern for any JSONPath query: start at $, narrow with a filter as early as possible (it's cheaper to filter the array once than to grab everything and post-process), then drill into the exact field you need.

Mistakes that cause a silent empty result

  • Forgetting the leading $. Every valid expression starts at the root - orders[*].total without the $ is not a valid path in most libraries.
  • Comparing a number to a quoted string in a filter. @.id == "101" when id is a number will not match - drop the quotes: @.id == 101.
  • Assuming $..price and $.price are the same. The single-dot version only matches price directly under the root; if it's nested, you need the recursive .. form.
  • Using a property with a dot or space without bracket notation. $.display name breaks - use $['display name'] instead.
  • Not confirming the array vs. object level first. If a path returns nothing, back it off one segment at a time ($.orders, then $.orders[*], then add the filter) to see exactly where it stops matching.

JSONPath vs. JMESPath vs. XPath

All three are query languages for structured data, but they're not interchangeable syntax for the same thing:

  • JSONPath - the most common choice for querying JSON in JavaScript/Node tooling and API testing tools; uses $, [?()], ...
  • JMESPath - used heavily in AWS CLI/SDK output filtering (--query); has its own pipe-based syntax (|) and built-in functions (sort_by(), contains()) that JSONPath doesn't have.
  • XPath - the original of the three, built for XML, with axis-based navigation (//, parent::) that predates JSON entirely.

If you already know one, the other two feel roughly similar in spirit - "select nodes matching a pattern" - but none of the operators are directly transferable; check the target tool's docs rather than assuming JSONPath syntax works in a JMESPath-based tool like the AWS CLI.

Try it

Paste your own JSON into the JSONPath Evaluator and test any expression from this cheat sheet directly against it - entirely in your browser, nothing uploaded.

FAQ

Is JSONPath an official standard?
There's an RFC (RFC 9535) that standardizes a core JSONPath syntax, but many libraries - including most JavaScript ones - predate it and add their own extensions (like =~ regex filters). Stick to what your specific library documents if you hit inconsistent behavior between implementations.

Why does my filter expression return nothing?
The single most common cause is an operator or type mismatch inside [?()] - for example comparing a number field to a quoted string, or forgetting that @ refers to the current item being tested, not the root. Isolate the problem by first running the path without the filter to confirm you're even at the right array.

What's the difference between $.a.b and $..b?
$.a.b is an exact path - it only matches b directly under a. $..b is recursive descent - it matches a property named b anywhere in the document, at any depth. The second is more powerful but slower and easier to get unexpected matches from on a large document.

Can JSONPath modify or delete data, not just read it?
The JSONPath language itself is read-only - it selects and returns matching values. Some libraries add write/delete helpers on top, but that's a library-specific extension, not part of JSONPath itself.

Top comments (0)