Skip to content

JSONPath Explained: Querying JSON by Path

Sep 2, 2026 · Formats & Standards

JSONPath is a compact query language that selects values out of a JSON document by describing the path to them, using $ for the root, dots or brackets to step into keys, [n] to index arrays, .. to search at any depth, and [?()] to filter. It is the JSON equivalent of XPath for XML: instead of writing loops to dig through nested objects and arrays, you write one expression that describes where the data lives. This article walks through the syntax against a single example document so each operator has a concrete meaning.

What problem does JSONPath solve?

JSONPath solves the problem of extracting specific values from a nested document without writing procedural traversal code. When JSON is small you can reach a value with a chain like data.store.book[0].title in your programming language. That approach breaks down when you want something more flexible: every price anywhere in the tree, only the books over a certain length, or the last element of a list. JSONPath expresses those selections declaratively. It shows up in API tools, in log processors, in configuration for CI systems, and in test assertions, wherever a document needs to be queried with a short string rather than a block of code. Throughout this article we will query one document:

{
  "store": {
    "book": [
      { "title": "A", "price": 8 },
      { "title": "B", "price": 22 },
      { "title": "C", "price": 15 }
    ],
    "bicycle": { "color": "red", "price": 120 }
  }
}

How do you select a single value?

Start every expression at the root with $ and step downward with dot or bracket notation until you reach the value. To reach the store object you write $.store. To reach the bicycle inside it you write $.store.bicycle, and to reach its colour you write $.store.bicycle.color, which selects "red". Dot notation is the readable default, but it cannot express keys that contain spaces, dots, or other awkward characters. For those you use bracket notation with a quoted key: $['store']['bicycle']['color'] selects exactly the same value. The two forms are interchangeable for ordinary keys and you can even mix them, so $.store['bicycle'].color is valid. Bracket notation is the escape hatch you reach for whenever a key would break the dotted form.

How do you work with arrays?

Index into arrays with brackets, where [0] is the first element, negative indices count from the end, and a colon expresses a slice. Selecting the first book is $.store.book[0], which returns the whole object { "title": "A", "price": 8 }. Reaching into it, $.store.book[0].title returns "A". Several array operators exist, and the table below summarises the common ones against the book array of three elements.

Expression Meaning Result on the book array
$.store.book[0] First element Book A
$.store.book[-1] Last element Book C
$.store.book[0,2] Elements 0 and 2 Books A and C
$.store.book[0:2] Slice, start to before 2 Books A and B
$.store.book[*] Every element Books A, B, C
$.store.book[*].price Price of every element 8, 22, 15

The wildcard * is the workhorse here. $.store.book[*].price reads as: every element of the book array, then its price, producing the list of all three prices. This is where JSONPath starts to save real effort, because that single expression replaces a loop.

What does recursive descent do?

The double-dot operator .. searches for a name at any depth, so you can collect a field without knowing exactly where it sits. In the example document, prices live in two different places: inside each book and inside the bicycle. The expression $..price finds all of them, returning 8, 22, 15, 120, because recursive descent visits every node in the tree and matches the price key wherever it appears. This is enormously useful for documents whose shape you do not fully control, such as third-party API responses, but it is also the operator to use with the most care. Because it walks the entire document, a broad expression like $..* selects nearly everything, and a recursive match can pull in values you did not intend if the same key name is reused for different purposes at different levels. Reach for .. when you genuinely want a field regardless of location, and prefer an explicit path when you know exactly where the value should be.

How do filter expressions work?

Filters select array elements that satisfy a condition, written as [?(...)] where @ refers to the element currently being tested. To select only books priced above ten, you write $.store.book[?(@.price > 10)], which evaluates the condition against each book in turn and keeps B and C. The @ symbol is the counterpart to $: where $ is the root of the whole document, @ is the root of the individual item under test. You can compare against numbers and strings, and combine conditions with logical operators in most implementations:

$.store.book[?(@.price > 10)]
  selects books B and C

$.store.book[?(@.title == "A")]
  selects book A

$.store.book[?(@.price > 10)].title
  selects the titles "B" and "C"

Filters are the most powerful part of JSONPath and also the least uniform across tools, so the exact operators available, and how they treat missing fields, vary by implementation. Test your filter against real data rather than assuming it behaves identically everywhere.

What are the common pitfalls?

The biggest pitfall is assuming every JSONPath implementation behaves identically, because for years there was no formal specification and libraries diverged. JSONPath began as an informal proposal, and different languages implemented the tricky parts, especially filters, slices, and what a query returns when nothing matches, in subtly different ways. RFC 9535, published in 2024, finally defines a formal standard, but many existing libraries were written before it and still follow the older informal behaviour. The practical consequences are worth listing:

  • Empty results differ. Some implementations return an empty list when a path matches nothing; others return null or raise an error. Do not rely on one behaviour across tools.
  • A path can return one value or many. An expression like $..price is a set of matches, so treat the result as a list even when you expect a single hit.
  • Filter syntax varies. Support for regular expressions, string functions, and combined conditions is not universal.
  • Root vs current context. Confusing $ and @ is a frequent mistake; inside a filter, conditions almost always begin with @.

Because of this variation, the reliable way to build a JSONPath expression is iteratively against the actual document. Paste the JSON into a client-side tool so it stays on your machine, format it with a JSON formatter so the structure is easy to see, and confirm your path selects exactly what you intend before wiring it into code. This matters for privacy as well as correctness, since API responses frequently carry tokens and personal data that should not be pasted into a remote service.

JSONPath turns nested-document traversal into a short, declarative string. Anchor at $, step through keys with dots or brackets, index and slice arrays, search any depth with .., and narrow with [?()] filters. Keep in mind that implementations still differ around the edges, build your expressions against real formatted data, and you will replace a lot of hand-written traversal code with a single readable path.

Frequently asked questions

What is JSONPath?

JSONPath is a query language for JSON, loosely modelled on XPath for XML. It lets you select values from a document with a compact path expression instead of writing procedural code to walk the structure by hand.

What does the dollar sign mean in JSONPath?

The dollar sign represents the root of the document. Every JSONPath expression begins at the root, so a path like $.store.book selects the book value inside the store object at the top level.

What is the difference between dot and bracket notation?

They select the same thing. Dot notation like $.store.book is shorter, while bracket notation like $['store']['book'] is required when a key contains spaces, dots, or other characters that would break the dotted form.

What does the double dot do in JSONPath?

The double dot is the recursive descent operator. It searches for a name at any depth in the document, so $..price finds every price field no matter how deeply it is nested.

Is JSONPath standardised?

It was originally an informal proposal and implementations drifted apart, but RFC 9535 published in 2024 defines a formal standard. Some older libraries still follow the earlier informal behaviour, so edge cases can differ between tools.