Skip to main content

JSONPath tester

Test JSONPath expressions against sample JSON with live match counts and per-node copy.

Free & unlimited
Mode
Samples
JSONPath expression
4 matches
JSON input
Matches
$.store.book[0].author
"Nigel Rees"
$.store.book[1].author
"Evelyn Waugh"
$.store.book[2].author
"Herman Melville"
$.store.book[3].author
"J. R. R. Tolkien"
All processing happens in your browser. No data is sent to any server.

Paste a JSON document on the left, type a JSONPath expression above it, and the matches appear on the right as an expandable tree — each one labelled with the concrete path it was found at, such as $.store.book[2].author, not just the expression you wrote. The match counter updates on every keystroke, so an expression that returns nothing tells you immediately rather than after a round trip. Invalid JSON is reported with the parser's own message and the character offset, which is usually enough to find a trailing comma without leaving the page. Three sample documents are built in: a bookstore for path and wildcard practice, a user list for filter expressions, and a small nested object for recursive descent.

Key facts about JSONPath tester

Key facts about JSONPath tester
Syntax supported$ root, dot child, bracket child with single or double quotes, wildcard as .* or [*], recursive descent .., array index, negative index, slice [start:end], and filter [?(...)]
Filter expressions@.field comparisons evaluated as JavaScript, so ==, !=, <, >, && and || all work — for example [?(@.age>30 && @.active==true)]
Filter depthOne level: @.role is substituted, @.address.city is not. Filters apply to arrays only
Not supportedUnion selectors such as [0,2], slice step [::2], script expressions like [(@.length-1)], and function extensions such as .length() or count()
Match pathsEvery result is shown with its resolved path, and each node inside the result has its own copy-path button
Match modesAll matches, or first match only — the counter always reports the full total
Result treeObjects and arrays collapse and expand, with key and item counts shown while collapsed; strings over 150 characters are clipped to one line until you click them
Type colouringStrings, numbers, booleans and null are coloured separately so a numeric value quoted as a string is visible at a glance
Sample documents3 — bookstore ($.store.book[*].author), users ($.users[?(@.role=="admin")].name) and a nested object ($..value)
InputTyped or pasted text, or the Paste button which reads the clipboard directly. There is no file picker and no drag-and-drop
Size limitNone imposed by the page — the practical ceiling is how much JSON your browser will parse and render in one tab
Error handlingA JSON syntax error and a bad path are reported separately, so you always know which half of the screen is wrong

What happens to your file

The JSON stays in the tab. Parsing is the browser's own JSON.parse, and the path evaluation is a tokeniser and a recursive walker written into the page bundle — there is no JSONPath service, no library downloaded at runtime and no request of any kind while you type. That matters here more than on most pages, because the document you are testing against is usually a real API response with real customer data in it. Nothing is written to local storage either, so a refresh clears both panels. Filter expressions are evaluated with a JavaScript Function built from your own input, which is why you should only paste filters you wrote or understand.

About this tool

  1. 1

    Load a document

    Paste your JSON into the left panel, use the Paste button to pull it from the clipboard, or start from one of the three samples while you work out the syntax.

  2. 2

    Check it parses

    If the JSON is malformed, the red box under the editor shows the browser's own parser message and position. Fix that first — no path can match a document that did not parse.

  3. 3

    Write the expression

    Start at $ and build downward: $.users to reach the array, $.users[*] for every element, $.users[*].email for one field of every element. The counter beside the field tells you how many nodes matched.

  4. 4

    Narrow it with a filter

    Add a predicate to select instead of enumerate: $.users[?(@.active==true)].name. Filters work on arrays and compare one field at a time.

  5. 5

    Read the resolved paths

    Each match is headed by the path it was actually found at. This is the fastest way to confirm that a recursive $..price is reaching the nodes you intended and not a similarly named field three levels down.

  6. 6

    Take the result with you

    Copy a single value, copy all matches as one JSON array, or copy an individual node's path to paste into your own code.

Specs & compatibility
ParserNative JSON.parse for the document; a hand-written tokeniser for the path
StandardThe widely used Goessner-style JSONPath, not the stricter RFC 9535 grammar — behaviour on edge cases may differ from a server-side library
Slice support[start:end] only; step is ignored and negative bounds are unreliable
Filter evaluationJavaScript expression semantics, so string comparison is exact and type coercion follows JavaScript rules
OutputExpandable tree per match, copy value per match, copy all matches as JSON, copy path per node
ClipboardRead (Paste button) and write (copy buttons) via the Clipboard API — Safari may prompt on read
NetworkNone after page load; works offline
BrowsersAny current browser; no WebAssembly, no worker, no file access
  • Build the expression one segment at a time and watch the match count. The segment where the count drops to zero is the segment that is wrong — usually a key that is nested one level deeper than you remembered.
  • Recursive descent is a blunt instrument: $..id matches every id anywhere in the document, including ones inside unrelated objects. Check the resolved paths on the results before you trust it in code.
  • String comparisons in filters are exact and case-sensitive. [?(@.role=="Admin")] will not match a value of admin.
  • Filters only reach one level. To select on a nested field, walk down to the array that contains it first rather than writing @.user.role inside the predicate.
  • Switch to First match when you only care whether anything matched at all; the counter still shows the true total, so you lose no information.
  • A negative index works for the last element: $.users[-1] is a shorter and safer way to say the end of the array than counting.
  • Use the copy-path button on a node you found by browsing the tree — it gives you a working expression for that exact node without writing one.
  • Different JSONPath implementations disagree on unions, steps and filter semantics. Once an expression works here, run it once against the library your service actually uses before shipping it.
  • Live match count while you type
  • Resolved path for every match
  • Filter, wildcard and recursive-descent support
  • Collapsible result tree with type colouring
  • Copy value, copy all, copy path
  • Clipboard paste for the document
  • Three worked sample documents
  • Work out the expression for a field in an API response before wiring it into a test assertion or an integration step.
  • Check what a JSONPath in an existing configuration file actually selects when the payload shape has changed.
  • Pull one field out of every element of a long array and copy the whole set as a JSON list.
  • Explore an unfamiliar response by browsing the tree and copying the path of anything interesting.
  • Debug an automation rule that silently matches nothing by testing its path against a real captured payload.
  • Teach JSONPath syntax with the bookstore sample, where each operator has a visible effect on the match list.
The first is an explicit path: it goes to the root object, into store, into book, and returns every element of that array. It only matches if the document really is shaped that way. The second is a recursive descent that searches the whole document for any key called book at any depth and returns each one it finds. Recursive descent is convenient on an unfamiliar payload and dangerous in production code, because a later schema change that adds a second book field anywhere will quietly change what your expression selects.
Check three things. Filters work on arrays, so the segment before the predicate must select an array, not an object. Only one level of field reference is substituted, so @.user.role inside the predicate will not resolve. And comparisons are exact JavaScript comparisons, so a string value must be quoted and matched case-sensitively, while a number must not be quoted. If any part of the expression throws, the element is simply treated as not matching rather than raising an error.
Not directly — this page has a text area and a clipboard Paste button, but no file picker and no drag target. For a small file, open it in an editor and copy the text. For a large one, the practical limit is your browser rather than the page: a few megabytes of JSON parses and renders fine, but a document with tens of thousands of matching nodes will make the result tree slow because every match is rendered as DOM.
It follows the common Goessner-style syntax that most libraries implement, which is what almost every tutorial and configuration format uses. It is not a conforming RFC 9535 implementation: union selectors, slice steps, script expressions and function extensions are not supported, and the standard defines some ordering and comparison details more tightly than this evaluator does. For everyday selection the two agree; for anything unusual, verify the expression against the library that will run it.
A negative index counts from the end, so $.users[-1] is the last element and $.users[-2] the one before it. A slice takes a start and an end and the end is exclusive: $.users[0:3] returns the first three elements. The step part of a slice is not supported here, so $.users[::2] will not give you every second element — enumerate with a wildcard and filter instead, or do the striding in your own code.
No. The document is parsed by your browser's built-in JSON.parse and evaluated by JavaScript that shipped with the page; no request leaves the tab while you type, and nothing is stored, so a refresh empties both panels. This is the point of testing paths here rather than in a hosted playground — API responses usually contain real identifiers, tokens or customer records, and the safest place for them is a tab that never opens a socket.
View all

Updated

We use anonymous analytics to improve ToolChamp. No personal data is stored or sold. Privacy Policy