JSON Formatter Hub

Free online JSON formatter, validator, and developer tools.

JSONPath: How to Query and Filter JSON Data

JSONPath is a query language for JSON, providing the same kind of document-navigation power that XPath gives XML developers. Instead of writing nested loops or chained property accesses, you write a compact expression that pinpoints exactly the data you need โ€” whether it lives three levels deep or is scattered across a hundred-element array.

What Is JSONPath and Why Does It Matter?

Stefan Goessner introduced JSONPath in 2007 as a direct analog to XPath 1.0 for XML. Just as XPath lets you write //book[@price<10] to pluck cheap books out of an XML document tree, JSONPath lets you write $.store.book[?(@.price < 10)] to do the same against a JSON structure. The syntax is deliberately terse and readable, making it a natural fit for configuration-driven data extraction.

The practical value of JSONPath is highest when you are working with API responses you do not fully control. Rather than parsing the entire payload and walking the object graph yourself, you hand a JSONPath expression to a library and get back exactly the nodes you care about. This approach is used by REST-testing frameworks like REST Assured in Java, by AWS's query language JMESPath in the AWS CLI, and by the wildly popular jq command-line tool (which has its own syntax but borrows heavily from the same ideas).

The Root Operator and Basic Navigation

Every JSONPath expression starts with $, which represents the root of the JSON document. Think of it as the outermost {} or []. From there you navigate using either dot notation or bracket notation.

Dot notation chains property names with dots:

$.store.book.title

This reads as: "start at root, enter the store property, then book, then title." Dot notation is concise and easy to read, but it cannot handle property names that contain spaces or special characters.

Bracket notation wraps property names in single quotes inside square brackets:

$['store']['book']['title']

Both forms are semantically equivalent for normal property names. Bracket notation is required when a key contains a hyphen, space, or starts with a digit โ€” for example $['content-type'].

Consider this sample document used throughout this article:

{
  "store": {
    "book": [
      { "title": "Sayings of the Century", "author": "Nigel Rees",    "price": 8.95  },
      { "title": "Sword of Honour",        "author": "Evelyn Waugh",  "price": 12.99 },
      { "title": "Moby Dick",              "author": "Herman Melville","price": 8.99  },
      { "title": "The Lord of the Rings",  "author": "J.R.R. Tolkien","price": 22.99 }
    ],
    "bicycle": { "color": "red", "price": 19.95 }
  }
}

The expression $.store.bicycle.color returns "red". The expression $.store.book[0].title returns "Sayings of the Century".

Wildcards and Recursive Descent

The wildcard operator * matches all properties of an object or all elements of an array at the current level. For example:

$.store.book[*].author

This returns every author value in the book array: ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J.R.R. Tolkien"]. You can also use $.* to return all top-level values of the root object.

The recursive descent operator .. (two dots) is one of JSONPath's most powerful features. It searches at any depth within the document, not just the current level. The expression:

$..author

Finds every author field anywhere in the entire document tree, regardless of nesting depth. This is invaluable when you do not know the exact structure of a deeply nested API response but you do know the key name you are looking for. The recursive descent can be combined with a property name, a wildcard, or an array index โ€” for instance $..[0] returns the first element of every array found anywhere in the document.

Array Slicing and Index Access

JSONPath supports Python-style array slicing. Here are the most useful forms:

$.store.book[0]      /* first book */
$.store.book[-1]     /* last book (negative index) */
$.store.book[0,2]    /* first and third books (union) */
$.store.book[0:2]    /* first two books (slice, exclusive end) */
$.store.book[1:3]    /* second and third books */
$.store.book[*]      /* all books (same as no index) */

The slice notation [start:end] follows the same convention as Python: the start index is inclusive and the end index is exclusive. Negative indices count from the end of the array, so [-1] always gives you the last element regardless of array length โ€” a cleaner approach than computing array.length - 1 in application code.

Filter Expressions

Filter expressions let you select array elements that satisfy a condition. The syntax is [?( expression )] where @ refers to the current element being tested:

/* books costing less than $10 */
$.store.book[?(@.price < 10)]

/* books that have an isbn property */
$.store.book[?(@.isbn)]

/* books by a specific author */
$.store.book[?(@.author == "Moby Dick")]

/* books priced between $8 and $13 */
$.store.book[?(@.price >= 8 && @.price <= 13)]

Filters are evaluated per element: @ is bound to each array element in turn, and only elements for which the expression evaluates to true are included in the result. The first expression above returns the two books priced at $8.95 and $8.99.

More complex real-world example โ€” extracting all users whose email domain is gmail.com from a users array:

/* Given: { "users": [ {"name": "Alice", "email": "alice@gmail.com"}, ... ] } */
$.users[?(@.email =~ /.*@gmail\.com$/)]

Note that regex support (=~) is available in some JSONPath implementations (notably the Jayway Java library) but is not part of the original Goessner specification. Check your library's documentation for which operators are supported.

Built-in Functions

The JSONPath 2.0 specification (RFC 9535, published 2024) formalizes a set of built-in functions. Older implementations vary, but these are widely available:

/* Number of books in the array */
$.store.book.length()

/* All keys of the store object */
$.store.keys()

/* Minimum price across all books */
$.store.book[*].price.min()

/* Maximum price across all books */
$.store.book[*].price.max()

/* String matching */
$.users[?( match(@.name, "A.*") )]

The length() function is especially useful in filter expressions: $.users[?( length(@.tags) > 2 )] returns only users who have more than two tags in their tags array. The match() and search() functions from RFC 9535 use I-Regexp patterns for string testing โ€” match() requires a full-string match while search() succeeds if the pattern matches anywhere in the string.

Practical Examples

Here are three patterns you will encounter repeatedly when working with real APIs.

Extract all email addresses from a user list:

/* Input */
{
  "users": [
    { "id": 1, "name": "Alice", "email": "alice@example.com" },
    { "id": 2, "name": "Bob",   "email": "bob@example.com"   },
    { "id": 3, "name": "Carol", "email": "carol@example.com" }
  ]
}

/* JSONPath */
$.users[*].email

/* Result */
["alice@example.com", "bob@example.com", "carol@example.com"]

Find all books under $15:

$.store.book[?(@.price < 15)]

/* Returns the three objects with prices 8.95, 12.99, and 8.99 */

Get the last element of an array (when length is unknown):

$.store.book[-1]

/* Returns the Lord of the Rings object */

Where JSONPath Is Used in Practice

REST Assured (Java) is a popular API testing library that ships with built-in JSONPath support. You write assertions like given().when().get("/books").then().body("store.book[0].title", equalTo("Sayings of the Century")) โ€” the path string is a JSONPath expression evaluated against the response body.

JMESPath is AWS's answer to JSONPath, used by the AWS CLI's --query flag. The syntax differs (no $ root, uses @ differently), but the concept is identical. You can filter EC2 instance lists with aws ec2 describe-instances --query "Reservations[*].Instances[?State.Name=='running'].InstanceId".

jq is the de facto standard for JSON processing on the command line. It has its own richer syntax but many of its core ideas โ€” .foo for property access, .[] for array iteration, select() for filtering โ€” map directly onto JSONPath concepts. Once you know JSONPath, picking up jq is straightforward.

Online formatters and validators increasingly offer JSONPath evaluation as a built-in feature. Paste your JSON, type a path expression, and see the extracted values highlighted. You can try this directly in our JSON Formatter.

JSONPath vs JavaScript Dot-Notation Chaining

In JavaScript, you can navigate JSON with plain property access: data.store.book[0].title. This works fine for deterministic paths, but it has real limitations. If any intermediate property is undefined, the expression throws a TypeError. You need data?.store?.book?.[0]?.title with optional chaining to be safe, and there is no built-in way to express "all elements" or "any depth."

JSONPath handles all of these cases with a single expression. It also returns an array of matches (potentially zero, one, or many), which maps naturally onto scenarios where you expect multiple results. For simple, known-path access, JavaScript dot notation is fine. For flexible querying, filtering, and deep searches across variable-shaped data, a proper JSONPath library is a far better tool.

Have questions about JSON syntax in general? Our JSON FAQ covers the most common developer questions about JSON structure and data types.

Summary

JSONPath gives you XPath-like query power for JSON data. The key operators to know are: $ (root), . and [] (navigation), * (wildcard), .. (recursive descent), array slices [0] / [-1] / [0:3], and filter expressions [?(@.price < 10)]. These seven tools cover the vast majority of real-world data extraction tasks. For testing APIs, querying cloud resources, or processing large JSON payloads on the command line, JSONPath (or its relatives like JMESPath and jq) is an essential part of the modern developer's toolkit.

Ready to test JSONPath expressions on your own data? Paste your JSON into our free JSON Formatter and explore the structure interactively.