JSONPath Tester

Run path expressions against JSON

Loading tool…

About JSONPath Tester

JSONPath Tester evaluates a JSONPath expression against a JSON document and prints the selected values as a JSON array. The document pane reports whether the JSON parses before you run anything, and the result is always an array, even when the query selected a single value or nothing at all.

JSONPath is not a standard in the way XPath is. Stefan Gössner described it in a 2007 article as a JSON analogue of XPath, and for seventeen years every implementation filled the gaps differently: whether filters may hold arbitrary expressions, what a negative index means, whether recursive descent enters array elements. RFC 9535 published a normative grammar in February 2024, but most libraries in production predate it and keep their historic behavior. This tester runs jsonpath-plus, so re-check anything you intend to ship against the library that will execute it.

How to use this developer tool

  1. Paste a representative document into the JSON pane and confirm the header reports it as valid.
  2. Type the expression into the JSONPath field, starting from the root symbol $.
  3. Run the query; the result is always a JSON array, so one match appears as a one-element array.
  4. Edit the fixture to include the awkward cases — a missing key, an empty array, a second element — and run the same expression again.
  5. Copy the expression into your code, then verify it once more against the JSONPath library your runtime uses.

Worked example: filtering an array and surviving a missing key

This catalogue has four books, one of which has no price at all, plus a bicycle that also has a price. The expression selects titles from books above a threshold.

Document and expression
Expression: $.store.books[?(@.price > 40)].title

{
  "store": {
    "books": [
      { "title": "Clean Code", "price": 30, "tags": ["craft"] },
      { "title": "Refactoring", "price": 45 },
      { "title": "Domain-Driven Design", "price": 52 },
      { "title": "Internal Draft" }
    ],
    "bicycle": { "color": "red", "price": 120 }
  }
}
Match result
[
  "Refactoring",
  "Domain-Driven Design"
]

The fourth book has no price, so @.price is undefined, the comparison is false, and no error is raised — a misspelled key produces exactly the same silence. The bicycle costs 120 and still does not appear, because the path is anchored at $.store.books; written as $..[?(@.price > 40)] it would have matched too.

JSONPath syntax, with results from the document above

Every expression in the third column was run against the catalogue in the worked example, so each row is reproducible by changing only the expression.

SyntaxWhat it selectsExample and result
$The document root; every expression starts here$ returns the whole object as a one-element array
.key or ['key']One named child of the current node$['store']['books'][1].title gives ["Refactoring"]
..keyThat key at any depth below the current node$..price gives [30, 45, 52, 120]
*Every child of the current node$.store.books[*].title gives all four titles
[n]One array element by zero-based index$.store.books[0] gives the first book object
[start:end]A slice, end exclusive$.store.books[0:2].title gives the first two titles
[-n:]The last n elements$.store.books[-1:].title gives ["Internal Draft"]
[a,b]A union of two indexes or names$.store.books[0,2] gives the first and third book
[?(expression)]Only elements for which the expression is truthy$.store.books[?(@.price > 40)].title gives two titles

Troubleshooting

An expression that works here returns nothing in the application.

Why it happens Dialect drift. Jayway JsonPath, Python's jsonpath-ng, the Go implementations, and RFC 9535 parsers disagree on filter syntax, on whether script expressions are allowed, and on how a filter behaves when the current node is an object rather than an array.

Fix Pin the behavior to the library you deploy: write the smallest fixture that reproduces the difference and run it through both. Prefer the conservative subset — property access, wildcards, indexes, slices, simple comparison filters — which nearly every implementation agrees on.

A misspelled key returns an empty array instead of an error.

Why it happens JSONPath has no schema and no notion of a required path. A query that selects nothing is a successful query with zero results, indistinguishable from one that was never going to match.

Fix Assert on the length of the result in consuming code rather than on the value at index zero. When debugging here, shorten the expression one segment at a time until values appear; the last segment you removed is the one that failed.

A negative index selects nothing.

Why it happens In jsonpath-plus a bare negative index such as $.store.books[-1] is not a from-the-end selector and returns an empty array, while the slice form $.store.books[-1:] does select the last element. Other libraries accept the bare form.

Fix Use [-1:] for the last element and [-2:] for the last two. For a dialect-neutral result, select the array itself and take the final item in application code.

The result is flat and you cannot tell which parent each value came from.

Why it happens The default result type is the matched value, so $..price returns four numbers with nothing to indicate that one of them belongs to the bicycle rather than a book.

Fix Select the containing objects instead of the leaf — $.store.books[?(@.price > 40)] without the .title suffix — and read the field you need from each one.

When this tool is the right choice

  • Pulling one field out of a deeply nested API response while debugging an integration.
  • Proving an extraction expression is correct before it goes into a test assertion, a Postman script, or a log pipeline.
  • Checking that an existing query still resolves after an upstream team changed the response shape.
  • Exploring an unfamiliar payload by widening an expression step by step until you find where a value lives.

When to use something else

  • Diagnosing why a document will not parse. This tool reports only valid or invalid; the JSON Formatter reports the exact offset of the syntax error.
  • Reshaping data. JSONPath selects existing values and cannot build new objects; use the JSON to Table Converter for tabular export, or jq for real transformation.
  • Comparing two payloads to see what changed. The Text Compare tool is built for that.

Privacy and data handling

The document is parsed and the expression evaluated in this browser tab by the jsonpath-plus library. Nothing is uploaded, nothing is stored between visits, and the filter expression is evaluated locally rather than sent anywhere. Production payloads routinely carry customer identifiers and bearer tokens, so trim the document to the shape you need before pasting it.

Related developer tools

Frequently asked questions

Is there an official JSONPath specification?

There is now. RFC 9535 was published in February 2024 and defines a normative grammar and semantics. It arrived seventeen years after the original article, so most deployed libraries were written against the informal description and still behave in their own way. Treat the RFC as the direction of travel, not as a description of your runtime.

Why is the result always an array?

The tester requests wrapped results, so a query selecting one value returns a one-element array and a query selecting nothing returns an empty array. That makes the empty case visible instead of showing a bare null, and it matches the default of most JSONPath libraries.

What is the difference between $.store.books and $..books?

The first is an exact path and finds books only as a direct child of store. The second is recursive descent and finds every books key at any depth. Recursive descent is convenient while exploring and risky in production, because a later schema change can add a second books key your query silently starts matching.

Can a filter contain a function call or a regular expression?

In jsonpath-plus, yes: [?(@.title.match(/^R/))] works because the expression is evaluated as JavaScript. That is an implementation feature, not a portable one. RFC 9535 defines a small fixed set of functions instead, and many libraries reject script expressions outright.

How is JSONPath different from jq?

JSONPath is a selection language: it names locations in an existing document and returns the values found there. jq is a transformation language with pipelines, arithmetic, conditionals, and object construction. Renaming a field or computing a total needs jq or application code, not JSONPath.

Does an empty result mean my JSON is wrong?

No. Invalid JSON is reported by the document pane before the query runs. An empty array means the document parsed and the expression matched nothing, which is a normal outcome — a missing optional field produces it as readily as a typo does.