Skip to content
jsonbeautifiers
English

JSONPath Tester

Write a JSONPath expression, see the matches live. RFC 9535 syntax.

Document
Matches

Nothing you paste leaves your browser. The connect-src allowlist makes that a browser guarantee rather than a promise. Check it yourself

Write a JSONPath expression, see every match against your own document, with the normalised path of each. Edit and run again until it selects what you meant.

The syntax here follows RFC 9535, the IETF Proposed Standard published in February 2024. That matters more than it sounds, because for seventeen years before it there was no specification at all.

Why the dialect has to be stated

JSONPath began as a 2007 blog post by Stefan Goessner. It was widely implemented and never specified, and the implementations diverged on nearly everything interesting: whether $.. includes the root, what a negative index means, whether $[0,1] unions, how a filter behaves when the key is missing, and what happens with a slice step of zero. A comparison project catalogued hundreds of these disagreements.

RFC 9535 settled it. A tester that does not say which dialect it implements is telling you the answer without telling you the question, so this one says: RFC 9535, with the exclusions listed below.

Syntax supported here

$
The root of the document. Every expression starts here.
.name and ['name']
A named member. Use the bracket form for names with spaces or punctuation.
.* and [*]
Every member of an object, or every element of an array.
..
A descendant segment: search at this level and every level below it.
[0] and [-1]
An array index. Negative counts from the end.
[1:5], [::2], [::-1]
A slice, with RFC 9535 semantics. A negative step walks backwards.
[0, 2, 'name']
Several selectors in one segment, producing the union of their results.
[?<expression>]
A filter. Inside it, @ is the current element and $ is the document root. Comparison with == != < <= > >=, combined with && || and !.
length() count() match() search() value()
The function extensions RFC 9535 defines. match() anchors the whole string; search() does not.

Deliberately not supported

Script expressions of the form [(...)] were never specified and RFC 9535 removed them. The parent operator ^ is an extension some implementations added and the RFC does not include. And @.length as a pseudo-property is the pre-RFC spelling of what is now length(@); if you paste one of these the tester says so rather than silently returning nothing.

JSONPath, JMESPath, jq and JSON Pointer

Four ways to address parts of a JSON document, for four different jobs.

JSONPath
Selects a set of nodes. Best when you want everything matching a pattern, at any depth. Now standardised as RFC 9535.
JMESPath
Transforms as well as selects: projections, multiselect hashes and pipe expressions let you reshape the output. Used by the AWS CLI. A real specification from the start.
jq
A full language with a query syntax attached. Reach for it when the operation is closer to programming than to selection.
JSON Pointer, RFC 6901
Addresses exactly one location, with no wildcards and no filters. Deliberately trivial, which is why JSON Patch and JSON Schema both use it. Two escapes: ~0 for a tilde and ~1 for a slash.

How to do this in code

Running the same query in code.

py Python
# jsonpath-ng is the most complete Python implementation
from jsonpath_ng.ext import parse

expr = parse('$.store.book[?(@.price < 10)].title')
titles = [m.value for m in expr.find(data)]

# JMESPath, if you prefer a specified language with projections
import jmespath
titles = jmespath.search('store.book[?price < `10`].title', data)
js JavaScript
import { JSONPath } from 'jsonpath-plus';

const titles = JSONPath({
  path: '$.store.book[?(@.price < 10)].title',
  json: data,
});

// Get the normalised paths rather than the values
const paths = JSONPath({ path: '$..author', json: data, resultType: 'path' });
sh jq

jq has no descendant-with-filter operator, so the two halves are written separately.

# The jq equivalent of a filtered descendant search
jq '.store.book[] | select(.price < 10) | .title' data.json

# Every value at any depth under a key
jq '.. | .author? // empty' data.json
java Java

Jayway JsonPath predates RFC 9535 and differs from it in places, notably around filters on missing keys.

import com.jayway.jsonpath.JsonPath;

List<String> titles = JsonPath.read(json, "$.store.book[?(@.price < 10)].title");

Questions

Why does my expression return nothing?
Usually one of three things: a name that needs bracket quoting because it contains a space or a dash, a filter comparing against an unquoted string (write @.type == 'book', not @.type == book), or a path that assumes an array where the document has an object. The tester reports a parse error with the position when the expression itself is malformed, and an empty result only when the expression is valid but matches nothing.
What is a normalised path?
RFC 9535 defines a canonical spelling for the location of a match: bracket-quoted names and numeric indices, as in $['store']['book'][0]['title']. Every match here shows one, which makes results comparable between implementations.
Is $..* the same as $..?
No, and this is one of the divergences the RFC settled. $..* selects every descendant node excluding the root; a bare $.. is not a complete expression at all.