Free JSONPath Tester — Evaluate Queries Online
Test JSONPath expressions against any JSON, 100% private in your browser — no upload, no signup, no eval. RFC 9535 standard engine plus Classic Goessner mode, with Values, Paths and Both result views.
What Is a JSONPath Tester?
A JSONPath tester is a tool that lets you write a JSONPath expression, paste a JSON document, and see exactly which nodes the expression selects — both the matched values and their precise locations — without writing code or running a script. For developers it shortens the loop from minutes to milliseconds: tweak the path, watch the result change, and ship the query with confidence.
JSONPath is a query language for JSON, the JSON analogue of XPath for XML. An expression is built from a small alphabet of selectors. $ is the root of the document. A dot or a bracket steps into a child: $.store or $['store']. The double dot .. is recursive descent — it searches every level of the tree. The wildcard * selects all elements or members. Brackets carry array indices ([0]), slices ([start:end:step]), unions ([a,b]), and filter expressions ([?(@.price < 10)], where @ is the element being tested). With those pieces you can pull a single field out of a deeply nested API response, assert on values in tests, drive data transforms in systems like Kubernetes, AWS Step Functions, and Azure Logic Apps, or extract structured data from irregular JSON — all without imperative traversal code. JSONPath is also famously inconsistent between implementations, which is exactly the problem a good tester surfaces before it reaches production.
This tester ships two engines. The default is an RFC 9535 engine: RFC 9535 is the IETF's February 2024 formal specification of JSONPath, the first time the language was precisely standardized after fifteen years of divergent implementations. It defines an exact grammar, the concept of normalized paths for results, and five standard functions — length(), count(), match(), search(), value(). Our RFC 9535 engine is a zero-dependency implementation that uses no eval, so it parses and interprets expressions with its own grammar instead of compiling them to JavaScript. The second engine is Classic (Goessner), the de facto 2007 dialect that most older online tools and libraries implement; switch to it to reproduce results from a tool like jsonpath.com or to run an expression you copied from legacy code. The two dialects agree on common paths but diverge in the edge cases — filter whitespace and quoting, union ordering, how missing members compare, and which functions exist — so being able to flip between them in one place is the fastest way to diagnose why an expression behaves differently than you expected.
What the tester surfaces beyond raw values: the result of a JSONPath query is a nodelist, and this tool can show it three ways. The Values view renders the matched nodes as a JSON array, exactly what you would consume in code. The Paths view renders each match's normalized path — a canonical, bracket-quoted location such as $['store']['book'][0]['title'] that uniquely identifies where in the document the value lives, no matter how the expression was written. Two expressions that select the same node produce the same normalized path, which makes the Paths view invaluable for debugging. The Both view shows values and paths side by side. A stats line reports how many nodes matched.
Security is a first-class concern here. Many online JSONPath evaluators run on a server, or embed a library that evaluates filter predicates with JavaScript eval — the design that produced remote-code-execution vulnerabilities tracked as CVE-2024-21534 and CVE-2025-1302 in widely used JSONPath packages. This tool uses no eval at all. The RFC 9535 engine has no eval path, and the Classic engine is built on a patched, pinned release of jsonpath-plus with eval explicitly disabled. That closes the RCE class of bugs and lets the tool run under a strict Content-Security-Policy that forbids unsafe-eval. Every evaluation is local: your JSON and your expression never leave the page, are never logged, and are never stored on disk — only your engine and view preferences persist to localStorage. That makes the tool safe for proprietary API payloads, redacted logs, internal config, and any data with a schema you would not paste into a server-backed service.
If JSON wrangling is your task, pair this with the other JSON tools on the site: format and pretty-print your input with the JSON Formatter, compare two documents with the JSON Diff, check a payload against a schema with the JSON Schema Validator, or turn a sample response into typed interfaces with JSON to TypeScript.
// The expression you build in this tester maps straight onto the
// RFC 9535 reference library used under the hood.
import { query, paths } from 'jsonpath-rfc9535';
const document = {
store: {
book: [
{ title: 'Sayings of the Century', author: 'Nigel Rees', price: 8.95 },
{ title: 'Sword of Honour', author: 'Evelyn Waugh', price: 12.99 },
{ title: 'Moby Dick', author: 'Herman Melville', price: 8.99 },
{ title: 'The Lord of the Rings', author: 'J. R. R. Tolkien', price: 22.99 }
]
}
};
// Values: query(document, path) returns the matched values directly.
const titles = query(document, '$.store.book[*].title');
// → ['Sayings of the Century', 'Sword of Honour', 'Moby Dick', 'The Lord of the Rings']
// Filter: books cheaper than 10.
const cheap = query(document, '$.store.book[?(@.price < 10)].title');
// → ['Sayings of the Century', 'Moby Dick']
// Normalized paths: paths(document, path) returns where each match lives.
const authorPaths = paths(document, '$..author');
// → ["$['store']['book'][0]['author']", "$['store']['book'][1]['author']", ...]
// RFC 9535 functions like length() are used INSIDE filters, not as a segment.
const longTitles = query(document, '$.store.book[?length(@.title) > 15]');
// → the two books whose title is longer than 15 characters Key Features
RFC 9535 Standard Engine (No Eval)
The default engine implements RFC 9535, the IETF's 2024 formal JSONPath specification — exact grammar, normalized paths, and the five standard functions. It is zero-dependency and uses no eval, so it parses and interprets expressions with its own grammar instead of compiling them to JavaScript. It runs under a strict Content-Security-Policy.
Classic (Goessner) Compatibility Mode
One toggle switches to a Goessner-compatible engine (built on jsonpath-plus, constructed with eval disabled) so expressions copied from older tools like jsonpath.com behave the way you saw them there. Flip between RFC 9535 and Classic to compare results and diagnose why a path matches differently across dialects.
Three Result Views: Values, Paths, Both
Values renders matched nodes as a JSON array, exactly what you consume in code. Paths renders each match's normalized path like $['store']['book'][0]['title']. Both shows them side by side so you can map every value to its precise location. The active view persists across sessions.
Normalized Paths for Every Match
Each result carries its canonical, bracket-quoted normalized path — the RFC 9535 way of uniquely identifying a node's location regardless of how the expression was written. Two expressions that hit the same node produce the same normalized path, which makes debugging ambiguous queries straightforward.
Full Selector Support
Root $, current element @, child .name, recursive descent ..name, wildcard [*], array index [0], slice [start:end:step], union [a,b], and filter [?()] expressions with comparison and logical operators. A built-in cheat sheet documents every selector so you never have to leave the page to look one up.
RFC 9535 Function Extensions
Call length(), count(), match(), search(), and value() inside expressions — count the elements of an array, test a string against an I-Regexp pattern, or filter on the size of a nested list. These standard functions are available in the default engine; the tool tells you to switch engines if you invoke them in Classic mode.
Format, Upload & Examples
Format JSON pretty-prints your input so the structure is readable before you query. Upload reads a .json or .txt file entirely in the browser — it is never sent anywhere. The Examples dropdown loads known-good starter expressions against sample data so you see the engine working before you adapt a path.
Permalink Sharing (No Upload)
Copy link encodes the JSON, expression, engine, and view into the URL hash. Browsers never transmit URL fragments in requests, so a shared link reproduces your full state on the recipient's machine without touching go-tools.org servers. Self-contained and audit-friendly for collaborative debugging.
100% Private, Browser-Only
Your JSON and your expression never leave your device. No network requests, no logging, no analytics on what you type — verify in DevTools → Network. Only the engine and view preferences persist to localStorage. Safe for proprietary payloads, redacted logs, and any data you wouldn't paste into jsonpath.com.
Worked Examples
Select every book title from a bookstore document
$.store.book[*].title
["Sayings of the Century", "Sword of Honour", "Moby Dick", "The Lord of the Rings"]
Paste the classic Goessner bookstore JSON, type the expression, and the Values view returns a JSON array of all four titles. Switch to Paths to see each result as a normalized path like $['store']['book'][0]['title']. The wildcard [*] iterates every element of the book array; .title projects one member from each.
Filter books cheaper than 10 with a filter expression
$.store.book[?(@.price < 10)].title
["Sayings of the Century", "Moby Dick"]
The filter selector [?()] keeps only array elements where the predicate is true; @ is the current element. Against the bookstore data (prices 8.95, 12.99, 8.99, 22.99) two books qualify. Both engines accept this shape, but note Classic (Goessner) writes the same filter as [?(@.price<10)] — switch engines if you copied an expression from an older tool.
Walk the whole tree with recursive descent
$..author
["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]
The .. operator descends into every level of the document and collects each author member wherever it appears, regardless of nesting depth. Recursive descent is the fastest way to pull one field out of a deeply nested or irregular structure without spelling out the full path.
Slice an array with [start:end:step]
$.store.book[0:2].title
["Sayings of the Century", "Sword of Honour"]
Array slices follow the same half-open [start:end] convention as Python and JavaScript: index 0 up to but not including index 2 returns the first two books. Add a third field for a step — $.store.book[::2] takes every other element. The end bound is exclusive, a common off-by-one trap the Paths view makes obvious.
Filter by title length with the RFC 9535 length() function
$.store.book[?length(@.title) > 15]
[{"title": "Sayings of the Century", "author": "Nigel Rees", "price": 8.95}, {"title": "The Lord of the Rings", "author": "J. R. R. Tolkien", "price": 22.99}] length() is one of the five RFC 9535 standard functions, and it is only valid inside a filter expression [?...] — never as a standalone path segment like $.store.book.length(), which the RFC 9535 grammar rejects (that segment form is a jsonpath-plus extension, not standard JSONPath). Here the filter keeps each book whose title has more than 15 characters; against the bookstore data, the titles longer than 15 characters are selected (for example "Sayings of the Century" and "The Lord of the Rings"), while shorter ones like "Moby Dick" and "Sword of Honour" are excluded. count(), match(), search(), and value() are likewise used inside filters. These functions are an RFC 9535 feature — switch to the standard engine (the default) to use them; Classic (Goessner) mode does not implement them.
Select a union of two named members
$.store.book[0]['title','author']
["Sayings of the Century", "Nigel Rees"]
A union selector [a,b] gathers several children in one expression. Here it pulls both the title and the author of the first book. Unions also work with array indices — [0,2] grabs the first and third elements. The Both view pairs each value with its normalized path so you can see exactly which member produced which result.
How to Use the JSONPath Tester
- 1
Paste or upload your JSON
Drop JSON into the input box, paste it, or click Upload to load a .json / .txt file from disk. Format JSON re-indents the document. Invalid JSON is flagged inline with a parser message before you query.
- 2
Choose an engine
RFC 9535 (the 2024 IETF standard, no eval) is the default. Switch to Classic (Goessner) when you are running an expression copied from an older tool such as jsonpath.com so the results match what you saw there.
- 3
Type your JSONPath expression
The leading $ is shown for you. Start with a child path like .store.book[*].title, an index like [0], a recursive descent like ..author, or a filter like [?(@.price < 10)]. Results update live as you type.
- 4
Switch the result view
Values shows a JSON array of matched values. Paths shows each match's normalized path like $['store']['book'][0]['title']. Both shows them side by side so you can map every value to its exact location in the document.
- 5
Copy the result or share a permalink
Copy result drops the output on your clipboard. Copy link encodes the JSON, expression, engine, and view into a URL hash (no upload) so a colleague can reproduce the exact query locally on their own machine.
Common JSONPath Mistakes
Forgot the leading $ root
Every JSONPath expression begins at the root, written as $. Omitting it (or writing the path as if $ were implicit) makes most engines reject the expression. The tester shows the $ for you, so start your input with the next selector — a dot, a bracket, or a recursive descent.
store.book[*].title → invalid (no root)
$.store.book[*].title → selects every title
Off-by-one slice — expected the end index included
Slices are half-open: [start:end] runs up to but not including end. [0:2] returns two elements (indices 0 and 1), not three. To include the last element by index, use [start:] or push the end bound one past it.
$.store.book[0:2] → first TWO books, not three
$.store.book[0:3] → first three books (indices 0,1,2)
Used a Classic expression in the RFC 9535 engine (or vice versa)
An expression copied from jsonpath.com or jsonpath-plus may parse or match differently under RFC 9535 because of filter, union, and function differences. If results look wrong, flip the engine toggle to match the dialect the expression was written for.
Classic filter run under RFC 9535 → parse error or unexpected nodes
Switch engine to Classic (Goessner) → reproduces the original result
Called an RFC 9535 function as a standalone segment
length(), count(), match(), search(), and value() are RFC 9535 function extensions that are only valid inside a filter [?...]. A standalone segment call like $.store.book.length() is rejected by the RFC 9535 grammar (it is a jsonpath-plus extension, not standard). Call the function inside a filter, and use the default RFC 9535 engine — the Classic (Goessner) engine does not implement these functions.
$.store.book.length() → parse error (not a valid RFC 9535 segment)
$.store.book[?length(@.title) > 15] → books with a title over 15 chars
Forgot @ inside a filter expression
Inside a filter [?()], the current element is @, not $. Writing $.price refers back to the document root rather than the element under test, so the filter selects nothing or everything. Use @ to address members of the element being filtered.
$.store.book[?($.price < 10)] → wrong scope
$.store.book[?(@.price < 10)] → books under 10
Quoted a member name with the wrong syntax
Bracket notation needs quotes around string keys: $['store'] or $.store both work, but $[store] (unquoted in brackets) is an index/identifier error. Use quotes inside brackets for any key with spaces, dots, or special characters: $['first name'].
$[store][book] → invalid bracket selectors
$['store']['book'] → same as $.store.book
Expected recursive descent to stop at the first level
$..author does not stop at the top — it collects every author member at every depth. If you only want direct children, spell out the path. Recursive descent over a large document can return far more nodes than intended.
$..price → every price anywhere in the tree
$.store.book[*].price → only book prices
Who Uses This Tool
- Extract Fields from an API Response
- Pull request IDs, nested resource attributes, or a list of names out of a JSON payload without writing traversal code. Build the path here against a sample response, confirm it returns exactly the nodes you want in the Values view, then paste the validated expression into your application or test.
- Write Assertions for Integration Tests
- Many test frameworks and contract-testing tools (REST Assured, Karate, Postman) use JSONPath to assert on response bodies. Draft the assertion path here, verify it selects the right node against a real response, and copy it into your test — catching a wrong path before the suite goes red.
- Configure Data Transforms in Pipelines
- Kubernetes, AWS Step Functions, Azure Logic Apps, and many ETL tools accept JSONPath to address fields in event payloads. Prototype the exact path against a representative event here, confirm it resolves, and drop it into your pipeline config with confidence that it points where you intend.
- Reproduce a jsonpath.com Result Privately
- Have an expression from a server-backed evaluator but cannot paste your data into a third-party site? Switch to Classic (Goessner) mode, load your JSON, and reproduce the same result locally — no payload ever leaves your browser, so proprietary data stays on your machine.
- Migrate Legacy Expressions to RFC 9535
- Moving to a system that advertises RFC 9535 compliance? Run a legacy expression in Classic mode, then flip to the RFC 9535 engine to see whether it still parses and matches the same nodes. The dual-engine comparison pinpoints filter, union, and function differences you would otherwise hit in production.
- Debug Why a Path Returns the Wrong Nodes
- A path that selects too much or too little is hard to reason about from the values alone. Switch to the Paths view to see the normalized location of every match — the exact array index, the exact member chain — and the off-by-one slice or stray recursive descent becomes obvious immediately.
- Teach or Review JSONPath
- Open a working expression against sample data and walk through it selector by selector, switching between Values and Paths so the learner sees both what is selected and where it lives. The cheat sheet and worked examples give a structured reference for code review or onboarding.
Engine & Algorithm Notes
- RFC 9535 Engine (jsonpath-rfc9535, Zero-Dependency)
- The default engine implements the IETF RFC 9535 grammar directly: it tokenizes and parses the expression into an abstract syntax tree and interprets it against the document. There is no eval and no Function constructor anywhere in the path, so it is immune to the eval-injection class of bugs and runs under a strict Content-Security-Policy.
- Classic Engine (jsonpath-plus, eval Disabled)
- The Classic (Goessner) engine is jsonpath-plus, pinned to a patched release (>= 10.4.0) and constructed with the eval option explicitly set to false. That keeps Goessner-dialect compatibility while closing the remote-code-execution vectors tracked as CVE-2024-21534 and CVE-2025-1302, which affected the library's default eval-based filter path.
- Normalized Path Generation
- Every match is reported with its RFC 9535 normalized path — a canonical form using single-quoted bracket notation ($['store']['book'][0]['title']) with array indices as bare integers. Normalized paths are stable and unique per node, so equivalent expressions yield identical paths, which the Paths and Both views rely on for unambiguous result identification.
- Lazy-Loaded Engine Chunks
- Both engines are loaded as separate JavaScript chunks only when first selected, so the initial page stays light and the engine you do not use is never downloaded. Switching engines re-evaluates the current expression against the current document immediately, with no page reload.
- Local File Reading and JSON Formatting
- The Upload button uses the browser FileReader API to read a .json or .txt file into the input entirely client-side — the file is never transmitted. Format JSON parses and re-serializes the input with two-space indentation, surfacing parse errors inline so malformed JSON is caught before evaluation.
- Permalinks via URL Hash (Never Transmitted)
- Share state is encoded in the location.hash fragment, carrying the JSON, the expression, the active engine, and the result view. Browsers never include the fragment in HTTP requests, so go-tools.org servers receive zero data when a permalink is opened; hydration happens entirely on the recipient's device.
JSONPath Best Practices
- Pick the Engine That Matches Your Target
- If your downstream system advertises RFC 9535 compliance, write and validate against the RFC 9535 engine. If you are reproducing or maintaining an expression from an older tool or library, use Classic (Goessner). Validating against the wrong dialect is the most common reason a path that worked in the tester fails in production.
- Verify with the Paths View, Not Just Values
- The Values view tells you what matched; the Paths view tells you where. When a query returns the right-looking values it may still be selecting them from the wrong location — a stray recursive descent or an over-broad wildcard. Check the normalized paths to confirm the expression hits exactly the nodes you intend.
- Mind the Exclusive End of a Slice
- [0:2] selects indices 0 and 1, not 0 through 2 — the end bound is exclusive, just like Python and JavaScript. Off-by-one slice errors are the most frequent JSONPath bug. Use the Paths view to read the exact index of every selected element and confirm the boundary before you ship.
- Prefer an Explicit Path Over Recursive Descent When You Can
- $..price is convenient but matches every price anywhere in the document, including ones you did not intend. When you know the structure, spell out the path ($.store.book[*].price) so the query stays precise and predictable as the data grows. Reserve .. for genuinely irregular or unknown shapes.
- Keep Whitespace and Quoting Consistent in Filters
- RFC 9535 follows its grammar exactly for filter expressions, while the classic dialect is looser. Write filters cleanly — quote string literals with single quotes ('fiction'), keep operators spaced, and avoid relying on lenient parsing — so the same expression evaluates the same way regardless of which engine or library eventually runs it.
Frequently Asked Questions
Is my JSON or JSONPath expression sent to your server?
What is JSONPath and what is it used for?
What is the difference between RFC 9535 and the classic Goessner syntax?
Why does the same expression return different results in the two engines, and how do I use an expression copied from jsonpath.com?
How do filter expressions [?()] work?
What does recursive descent (..) do?
What are the RFC 9535 functions length(), count(), match(), search(), and value()?
How do array slices [start:end:step] work?
What is a union selector and how do I select multiple keys at once?
Can I share a JSONPath query and its JSON via a link?
Is there a maximum JSON size?
How is this different from jsonpath.com and is it safe — no eval?
What do the Values, Paths, and Both views show?
Does this work offline, and what about a Content-Security-Policy?
Related Tools
View all tools →Base64 Decoder & Encoder
Encoding & Formatting
Decode and encode Base64 online for free. Real-time conversion with full UTF-8 and emoji support. 100% private — runs in your browser. No signup needed.
Base64 to Image Converter
Encoding & Formatting
Decode a Base64 string or data URI back into an image in your browser. Preview, read dimensions & MIME, then download as PNG, JPG, GIF, SVG. No upload.
CSV to JSON Converter
Encoding & Formatting
Convert CSV to JSON in your browser. RFC 4180, type inference, header row, big-int safe. 100% private, no upload.
.env to JSON Converter
Encoding & Formatting
Paste a .env file, get JSON instantly. Your database passwords, API keys and tokens never leave your browser — 100% private, no upload, free dotenv parser.
HTML to Markdown Converter
Encoding & Formatting
Convert HTML to clean Markdown in your browser — GFM tables, task lists, and links. Choose ATX/Setext headings and inline or reference links. Great for migrating web content or feeding LLMs. 100% private, no upload.
Image to Base64 Converter
Encoding & Formatting
Convert images to Base64 data URIs in your browser — PNG, JPG, GIF, WebP, SVG, ICO. Copy HTML, CSS, Markdown & JSON, with the exact size increase. 100% private, no upload.