JSONPath Tester
Test JSONPath queries against your JSON, with every match and its path, following RFC 9535.
Runs a JSONPath query on your JSON and lists what it selects, as values or as Normalized Paths such as $['store']['book'][0], with each match tinted where it sits in the input. Place the cursor on any value to get the path that selects it.
Queries follow RFC 9535, the JSONPath standard published in 2024, and the tool passes every case of the JSONPath Compliance Test Suite. Expressions written for older dialects — Jayway, JMeter, kubectl — get an error that gives the standard way to write them. It runs in your browser: nothing you paste or open is sent anywhere.
How to use it
- JSONPath query — runs as you type. An error names its column. Try fills in an example query, and the bookstore document of RFC 9535 when the JSON field is empty; Syntax lists everything a query can hold.
- JSON — typed, pasted, dropped or opened from a file. Matches are tinted by their exact characters, so in minified JSON the value is marked, not the whole line; a match inside another match is covered by the outer tint.
- At the cursor — the Normalized Path of the value or member the cursor is in; Use as query puts it in the query field.
- Matches — a JSON array of the selected values, or with Paths of their Normalized Paths, in the order RFC 9535 gives them, with the count beside. Copy and Download carry what is shown.
Values keep their numbers as written: 1.0 stays 1.0, and integers past 253 keep every digit. A key written twice in one object takes its last value, with a warning. A query that runs too long on a large document is stopped.
How a query is evaluated
A query starts at the root, $, and each segment after it takes the nodes selected so far to a new list: a member by name, an item by index, a slice, every child (*), or the children a filter holds for. A descendant segment (..) does the same at every depth, visiting a node before its children and children in document order.
A filter tests each child of a node, with @ standing for that child. Comparisons follow RFC 9535: numbers by value, so 1 == 1.0 holds, compared here exactly from their digits rather than as floating point; strings by Unicode code point; arrays and objects by their contents. A missing member is “Nothing”, which equals only another Nothing, so @.isbn == null is false for a book without an isbn, while !@.isbn is true. Comparing a string with a number is false, never an error.
match() and search() take I-Regexp (RFC 9485), a small regular-expression language every platform can run alike: no \d, \w or lookarounds — write [0-9] — and . matches any character but a line break. A pattern outside I-Regexp makes the test false, as the RFC requires, and a note under the query names it.
A query is checked before it reads the JSON: only a query for one node (names and indexes, such as @.price) can be compared, and functions must get the kind of argument they take, so $[?@.* == 1] is an error rather than an empty result.
Examples on the bookstore document
Titles of the books under 10
- JSONPath query
- $..book[?@.price < 10].title
- Matches
[ "Sayings of the Century", "Moby Dick" ]
2 matches. The filter keeps the books whose price is under 10, and .title then takes each one's title.
Books with an ISBN, as paths
- JSONPath query, Paths
- $.store.book[?@.isbn]
- Matches
[ "$['store']['book'][2]", "$['store']['book'][3]" ]
A query alone inside a filter tests that the member exists, whatever its value.
The last two books' authors
- JSONPath query, Paths
- $.store.book[-2:].author
- Matches
[ "$['store']['book'][2]['author']", "$['store']['book'][3]['author']" ]
A negative start counts from the end; the paths give the index from the start, as Normalized Paths always do.
A title search with an I-Regexp
- JSONPath query
- $..book[?search(@.title, '[Rr]ing') || length(@.title) < 10].title
- Matches
[ "Moby Dick", "The Lord of the Rings" ]
search() looks for the pattern anywhere in the title; length() counts its characters.
JSONPath syntax
| Syntax | Selects or means |
|---|---|
$ | The root of the document |
@ | The item a filter is testing |
.name ['name'] | A member by name |
[0] [-1] | An item by index; negative counts from the end |
[1:3] [::2] [::-1] | A slice: start, end (not included), step |
* [*] | Every member or item |
..name ..* | The same, at every depth |
['a','b'] [0,2] | Several at once, in order |
[?@.price < 10] | A filter: the items the test holds for |
== != < <= > >= | Comparisons |
&& || ! ( ) | And, or, not, grouping |
[?@.isbn] | Items that have the member |
length(@.tags) | Characters of a string, items or members |
count(@.*) | How many nodes a query selects |
match(@.id, 'A[0-9]+') | The whole string matches an I-Regexp |
search(@.title, 'Ring') | Part of the string matches |
value(@..color) | The single value a query selects |
Standards
Queries, Normalized Paths and comparisons follow RFC 9535; patterns in match() and search() follow RFC 9485, run as its section 5.3 maps them to JavaScript.
Sources
- RFC 9535: JSONPath: Query Expressions for JSON — IETF
- RFC 9485: I-Regexp: An Interoperable Regular Expression Format — IETF
- JSONPath Compliance Test Suite — jsonpath-standard
Reviewed
Frequently asked questions
Before RFC 9535, every library read JSONPath its own way, from Stefan Goessner's 2007 article: Jayway (the Java library JMeter and REST Assured use) added .length(), =~ and in, and kubectl wraps paths in { } templates. The tool answers each with the standard form:
- $..book[?(@.author =~ /.*Tolkien/)]
- RFC 9535 has no =~; test a pattern with match(@.name, "J.*") or search(@.name, "J") (column 20)
- $.store.book.length()
- Functions are not methods in RFC 9535: write length(@.items) inside a filter, such as $[?length(@.items) > 2] (column 20)
- $..book[?(@.category in ['fiction'])]
- "in" is not an RFC 9535 operator; combine comparisons with || and &&, such as @.a == 'x' || @.a == 'y' (column 22)
- {.items[*].metadata.name}
- kubectl's { } braces wrap a template; write the path alone, starting with $, such as $.items[*] (column 1)
Filters in parentheses, ?(@.price < 10), are standard and work as written.
@.tags.length selects a member named length, which arrays do not have. Older dialects read it as the array's size; the standard writes that as $[?length(@.tags) > 2]. The tool notes it when a query using .length selects nothing.See also
- JSON Formatter & Minifier — to check and format the JSON first.
- JSON Schema Validator — to check the whole document against a schema.