Skip to content

JSONPath Explained: Query Your JSON

JSONPath syntax — root, child, recursive descent, slices, filters — with real queries against a sample document. Plus jq and JMESPath comparison.

JSONPath is to JSON what XPath is to XML: a small expression language for picking values out of a document. It's standardized in RFC 9535 (2024) and supported by every JSON tool worth using. If you've ever wanted to say "give me every order with status: shipped and a total above $100", JSONPath is the shortest path.

What JSONPath is

A JSONPath expression is a string that describes a set of locations in a JSON document. The evaluator walks the document and returns either the matching values, the matching paths, or both. The grammar is small enough to memorise — root, child, descent, index, slice, filter.

Throughout this guide we'll query this sample document:

{
  "store": {
    "books": [
      { "title": "Pragmatic Programmer", "price": 30, "tags": ["dev"] },
      { "title": "Designing Data-Intensive Apps", "price": 45, "tags": ["dev", "data"] },
      { "title": "The Art of Travel", "price": 12, "tags": ["essays"] }
    ],
    "bicycle": { "color": "red", "price": 199 }
  }
}

Root, child, recursive descent

Three operators do most of the work:

  • $ — the root of the document.
  • .name or ["name"] — child access. Both forms are equivalent; the bracket form lets you use names with special characters.
  • ..name — recursive descent. Matches name at any depth.
ExpressionResult
$the whole document
$.store.bicycle{ "color": "red", "price": 199 }
$["store"]["bicycle"]same as above
$..price[30, 45, 12, 199] — every price in the document
$.store..title["Pragmatic Programmer", "Designing Data-Intensive Apps", "The Art of Travel"]

Recursive descent is the single most useful operator. When you don't know the exact path, $..fieldname finds every occurrence.

Array indices, slices, and wildcards

Arrays use bracket notation:

  • [0], [-1] — by index (negative counts from the end).
  • [0, 2] — multiple indices.
  • [1:3] — slice (Python-style, half-open).
  • [*] — wildcard, every element.
ExpressionResult
$.store.books[0]the first book object
$.store.books[-1].title"The Art of Travel"
$.store.books[0:2]the first two books
$.store.books[*].titleall three titles
$.store.books[*].tags[*]["dev", "dev", "data", "essays"]

The wildcard * also works on objects: $.store.* returns every value under store (here, the books array and the bicycle object).

Filter expressions

Filters are predicates over array elements. The syntax is [?expression], where @ refers to the current element.

ExpressionResult
$.store.books[?(@.price < 20)][{ "title": "The Art of Travel", ... }]
$.store.books[?(@.price >= 30)].titlefirst two titles
$.store.books[?(@.tags.contains("data"))].title["Designing Data-Intensive Apps"] (implementation-dependent)
$..[?(@.color == "red")][{ "color": "red", "price": 199 }]

Supported operators in filters: comparison (==, !=, <, <=, >, >=), boolean (&&, ||, !), and existence (just @.name — truthy if the key exists). Some implementations add regex (=~) and string functions (contains, match, length); these are non-standard so check your library.

Common recipes

  • Find a record by id$.users[?(@.id == "user_123")].
  • Pull a field from every record$.users[*].email.
  • Find every error anywhere$..error.
  • First N items$.items[:10].
  • Last item$.items[-1].
  • All keys at the top level$.* (object) or $[*] (array).

For exploratory work, the JSON Viewer lets you click into a node and copy the JSONPath that reaches it — much faster than typing expressions blind.

JSONPath vs jq vs JMESPath

Three query languages cover overlapping ground:

  • JSONPath — XPath-like, standardized in RFC 9535. Best for finding values. Limited transformation.
  • jq — a full Turing-complete language. Best for transforming JSON in shell pipelines. Filters, map/reduce, string operations, arithmetic. Mandatory CLI tool.
  • JMESPath — AWS's query language, used by every AWS CLI command (--query). Similar power to JSONPath plus projections and multi-select.

Rule of thumb: JSONPath in your code, jq in your shell, JMESPath if you're already in the AWS ecosystem.

Try it

Open the JSON Viewer, paste a payload, and click any node — the tool copies the JSONPath that reaches it. For ad hoc filtering, type an expression in the search box.

Next steps