Per-match source pathsFilters and slicesPositioned syntax errors

JSONPath Tester

Test a JSONPath expression against your own document and see every match with its value, type and the exact path it came from. All processing happens locally in your browser.

Query JSON

Test a JSONPath expression against your own document

Every match is listed with its value, its type and the exact path it came from, so you can tell which array index or key produced each result rather than just seeing a flat list.

Matches2
Document depth5 levels
Objects7
Arrays1
ExpressionJSONPath
Example expressions
DocumentJSON to query
OutputMatched values
[
  "Sayings of the Century",
  "Moby Dick"
]

2 matches found. The table below shows where each one came from.

Match detailValue, type and source path
Each match with its type and the path it was found at
#ValueTypePath
1"Sayings of the Century"string$['store']['book'][0]['title']
2"Moby Dick"string$['store']['book'][2]['title']

What is JSONPath?

JSONPath is a query language for JSON, in the same way XPath is a query language for XML. Instead of writing loops to walk a structure, you describe the shape of what you want and get back every value that fits.

Every expression starts at $, the document root, and then navigates:

$.store.book[0].title        one specific title
$.store.book[*].title        every title in the array
$..title                     every title anywhere in the document
$.store.book[?(@.price < 10)] every book cheaper than 10

The value of this over hand-written loops is that a single expression handles arbitrary depth and arbitrary array lengths, and it survives a document growing an extra level of nesting.

Which dialect this page uses: the evaluator is jsonpath-plus. Its core syntax follows Stefan Goessner's original 2007 proposal, which is what most language ports implement. It also adds a handful of useful operators that are not in that proposal and not in RFC 9535 — ~, ^, @property, @path and .length. Those are marked as extensions below, because an expression using them will not run in a Java or Python JSONPath library.

How to test an expression

  1. Paste your JSON Invalid JSON is reported with the line and column, plus the text of the offending line, so you can find an unquoted key or a trailing comma quickly.
  2. Write the expression Results update as you type. A syntax error names the position in the expression where parsing stopped.
  3. Read the match table Each row shows the value, its JSON type, and the bracket path it came from — $['store']['book'][2]['title']. That is how you tell which array element produced a given result.
  4. Switch the output format Matched values for the data itself, bracket paths for locating things, or JSON Pointer if you need RFC 6901 references for a patch operation.

Selectors, from simplest to most useful

Child access

$.store.name         dot notation
$['store']['name']   bracket notation — identical result

Bracket notation is not just a style preference. It is required when a key contains a space, a dot, a dash or any character that would break dot notation: $['user-id'] works, $.user-id does not.

Array indices, unions and slices

$.store.book[0]        first element
$.store.book[-1]       last element
$.store.book[0,2]      union: first and third
$.store.book[:2]       slice: index 0 and 1
$.store.book[1:3]      slice: index 1 and 2, not 3
$.store.book[*]        every element

Slices follow the same half-open convention as Python and JavaScript: the end index is excluded. [1:3] returns two elements.

Recursive descent

.. is the operator that makes JSONPath worth using. It searches every level below the current point:

$..price     every "price" key anywhere, at any depth
$..book[*]   every element of any array called "book"

Against the sample document, $..price returns four values: three book prices and the bicycle price. Note that it flattens across different parents — the result tells you the values but not where they came from, which is exactly why the match table here shows the path for each one.

Filters

A filter is written [?(...)]. Both the ? and the parentheses are required, and @ refers to the current item:

$.store.book[?(@.price < 10)]                 numeric comparison
$.store.book[?(@.category == "fiction")]      string equality
$.store.book[?(@.isbn)]                       key exists and is truthy
$.store.book[?(@.price > 10 && @.category == "fiction")]   combined
$.store.book[?(@.inStock == false)]           explicit boolean

Two details catch people out. String comparisons need quotes — @.category == fiction fails. And [?(@.isbn)] is a truthiness test, not an existence test: a book with "isbn": "" or "isbn": 0 would be excluded. To test presence properly you need the @property extension.

Extensions specific to this evaluator

Expression Returns Portable?
$.store.*~ The key names in store, not their values No
$..book[?(@.price > 10)]^ The parent of each match No
$..[?(@property === "price")] Matches by key name rather than value No
$.store.book.length The array length as a number No

These are genuinely handy, and using them is fine as long as the expression stays in JavaScript. If it is going into a Java, Python or Go service, stick to the portable selectors.

Examples against the sample document

The loaded sample is the classic bookstore document. Here is what each expression returns from it:

Expression Matches Result
$.store.book[*].author 4 All four author names
$..price 4 8.95, 12.99, 8.99, 22.99 plus the bicycle at 19.95
$.store.book[?(@.price < 10)].title 2 "Sayings of the Century", "Moby Dick"
$.store.book[?(@.isbn)].title 2 Only the two books that carry an ISBN
$.store.book[:2].title 2 The first two titles
$.store.*~ 3 "name", "book", "bicycle"
$.store.book.length 1 4

Notice that $..price returns five values in total across books and the bicycle. That is recursive descent doing its job, and it is also the most common source of surprise — it does not respect the shape you had in mind, only the key name.

Common mistakes

  • Omitting $. store.book[0] is not a valid expression. Every path is anchored at the root.
  • Forgetting [*] before a child. $.store.book.title returns nothing, because book is an array and has no title key. You need $.store.book[*].title.
  • Writing a filter without ?. [(@.price < 10)] is a syntax error. The form is [?(...)].
  • Unquoted strings in a filter. [?(@.category == fiction)] fails; the value needs quotes.
  • Expecting [?(@.key)] to test existence. It tests truthiness, so empty strings, zero and false are excluded.
  • Assuming an empty result means an error. It does not. JSONPath returns an empty array when nothing matches, exactly as a database query returns no rows.
  • Expecting one value from a filter. Results are always a list, even when there is a single match. In code, index into it or take the first element.
  • Using dot notation on an awkward key. $.data.user-id is parsed as a subtraction. Use $.data['user-id'].

Use cases

  • Pulling a value out of a deep API response. Faster than writing nested optional-chaining to find where a field lives.
  • Writing an API test assertion. Postman, Karate, REST Assured and Newman all use JSONPath for response assertions, so an expression verified here transfers with minor dialect care.
  • Configuring a log pipeline. Fluentd, Logstash and many SIEM tools extract fields by JSONPath.
  • Reading a Kubernetes resource. kubectl get pods -o jsonpath=... uses a related dialect; the portable selectors work there.
  • Auditing a document for a field. $..apiKey across a config export quickly reveals whether a secret is present somewhere unexpected.
  • Building a JSON Patch. The JSON Pointer output gives you RFC 6901 paths directly usable in a patch operation.

If the JSON itself needs work first, the JSON Formatter will indent and validate it, and the JSON Schema Validator checks it against a schema. To convert the extracted result, JSON to YAML and JSON to CSV take it further.

Privacy and limits

Both the document and the expression stay in your browser. Evaluation runs on the page and nothing is uploaded, which matters when the payload is a production API response.

The match table and copied output are capped at 500 results so a broad recursive expression against a large document cannot stall the page. Usage analytics record only the output format and a rough match-count band, never the document or the expression.

Frequently Asked Questions

Why does my expression return an empty array instead of an error?

Because no match is a valid outcome in JSONPath, exactly as a database query can legitimately return no rows. If you expected results, the usual causes are a misspelled key, a missing [*] before a child of an array, or a filter comparing a string without quotes.

Will an expression from this page work in Java or Python?

The portable selectors will: $, dot and bracket access, indices, unions, slices, wildcards, recursive descent and filters. The extensions will not — ~, ^, @property, @path and .length are specific to jsonpath-plus. The syntax table marks which is which.

What is the difference between $.store.book[*].title and $..title?

The first is precise: titles of books inside store, and nothing else. The second finds every key called title anywhere at any depth, including places you did not intend. Recursive descent is powerful but indiscriminate, which is why the match table shows the source path for each result.

How do I filter on more than one condition?

Combine with && and ||, as in [?(@.price > 10 && @.category == "fiction")]. Parentheses group as you would expect. Keep in mind that a filter operates on one item at a time, so it cannot compare an item against a different item.

How do I test whether a key exists rather than whether it is truthy?

[?(@.key)] is a truthiness test, so "" and 0 and false all fail it. For genuine presence, use the @property extension: $..[?(@property === "key")]. That is a jsonpath-plus feature and will not port to other libraries.

What is JSON Pointer and when should I use that output?

JSON Pointer (RFC 6901) is a simple slash-separated path such as /store/book/0/price. It is the format JSON Patch operations use, so choose it when you intend to modify the document rather than just read from it. Bracket paths are better for reading and debugging.

Is JSONPath standardised?

Only recently and only partially. The widely implemented version is Goessner's original proposal from 2007, which was never a formal standard, so ports diverged. RFC 9535 was published in 2024 to define a normative subset, but most libraries — including this one — predate it and support a superset.