The JSON Cheatsheet: This Tool, jq, and JSONPath on One Screen
One screen. Print it or pin the tab. Everything here was run on jq 1.7 and Node 24 before it was written, so the outputs are real, not remembered.
This tool, honestly
Two panes: paste on the left, formatted output on the right. Validation runs on every keystroke — no submit button, no upload, nothing leaves the browser.
| Control | What it does |
|---|---|
| Format (2 spaces) | JSON.stringify(parsed, null, 2) |
| Format (4) | Same, four-space indent |
| Minify | JSON.stringify(parsed) — no whitespace |
| Auto-Repair | Single quotes → double, drops trailing commas, quotes naked keys |
| Sample / Clear / Copy | Demo payload, empty both panes, copy output |
The status bar tells you array length and byte size; the footer reports line and character counts. On failure, the output pane carries the engine's own error text — V8's position N, which you convert with one line:
const m = err.message.match(/position (\d+)/);
const upto = raw.slice(0, +m[1]);
const line = upto.split("\n").length, col = upto.length - upto.lastIndexOf("\n");
There are no keyboard shortcuts. No Ctrl+Enter, no key handlers of any kind — buttons only. Say so plainly rather than let you hunt for one. Auto-Repair is a diagnostic, not a pipeline: three regex substitutions will happily rewrite an apostrophe inside a value. Fix the source. For the deeper null vs missing, number-precision, and date-format decisions, see JSON best practices.
jq: twelve one-liners
Sample document, so the outputs below are reproducible:
{"users":[{"id":1,"name":"ada","role":"admin","tags":["math","eng"],"joined":"2024-01-05"},
{"id":2,"name":"linus","role":"dev","tags":["kernel"],"joined":"2023-06-11"},
{"id":3,"name":"grace","role":"admin","tags":["navy","compilers"],"joined":"2025-02-20"}],
"meta":{"total":3,"page":1}}
# 1. Project — pick fields
jq -c '[.users[] | {id, name}]' # [{"id":1,"name":"ada"},…]
# 2. Filter
jq -c '[.users[] | select(.role=="admin") | .name]' # ["ada","grace"]
# 3. Delete a key everywhere it appears
jq -c 'del(.. | select(has("tags")?) | .tags)' # tags gone at any depth
# 4. Rename a key (jq has no rename; map the entries)
jq -c '.users |= map(with_entries(if .key=="joined" then .key="joinedAt" else . end))'
# 5. Every scalar path, dotted
jq -c 'paths(scalars) | join(".")' # users.0.id, users.0.name, …
# 6. Where is this value? (path of every match)
jq -c 'paths(scalars) as $p | select(getpath($p)=="admin") | $p | join(".")'
# # users.0.role, users.2.role
# 7. "grep" for any string matching a pattern
jq -c '.. | strings | select(test("kernel"))' # "kernel"
# 8. Count leaves — a fast shape summary
jq -c '[paths(scalars)] | length' # 19
# 9. Group and count
jq -c '.users | group_by(.role) | map({role: .[0].role, n: length, names: [.[].name]})'
# 10. Distinct values across a nested array
jq -c '[.users[].tags] | flatten | unique' # ["compilers","eng","kernel","math","navy"]
# 11. Argument-injected filter (no shell interpolation, no injection)
jq -c --arg r admin '[.users[] | select(.role==$r) | .id]' # [1,3]
# 12. Transform every string, recursively
jq -c '.users |= walk(if type=="string" then ascii_downcase else . end)'
Two traps cost people the most hours: group_by needs the array, so .users | group_by(.role) works and group_by(.role) on the root object throws Cannot index array with string "role". And input_line_number aborts on the first malformed NDJSON line rather than reporting it — use jq -c . file 2>&1 to see which line broke.
JSONPath: the same tasks, minus the ones it can't do
JSONPath (RFC 9535) is a selector language. It selects. It does not reshape, rename, delete, or aggregate — implementations bolt those on and disagree about how.
| Task | jq | JSONPath |
|---|---|---|
| All names | [.users[].name] | $..name |
| Filter equal | select(.role=="admin") | $.users[[email protected]=='admin'].name |
| Filter numeric | select(.id>1) | $.users[[email protected]>1].id |
| Wildcard children | .users[*] | $.users[*] |
| Slice | .[0:2] | $.users[0:2:1] |
| Union | // alternative | $.users[0,2].name |
| Regex | select(.name|test("^a")) | $.users[[email protected]=~'^a'] |
| Parent of match | .. | select(.=="x") | $..* + host code |
| Delete / rename / group | del, with_entries, group_by | not in the language |
Use JSONPath when you're reading — a config explorer, a test assertion, a UI's data binding. Use jq the moment you need to compute. Full comparison in JSON tools compared.
Validate or crash
Fail the build, fail the deploy, fail the CI step — before the payload reaches a client.
# jq: exit 0 valid, nonzero invalid, -e keeps output off stdout
jq -e . config.json > /dev/null || exit 1
# Python stdlib, no third-party imports
python3 -m json.tool config.json > /dev/null || exit 1
# Node, no dependencies — reads stdin, exit 1 on bad JSON
node -e 'JSON.parse(require("node:fs").readFileSync(0,"utf8"))' < config.json
In-process, throw loudly instead of swallowing:
function mustParse(text, where) {
try { return JSON.parse(text); }
catch (e) { throw new SyntaxError(`${where}: ${e.message}`); }
}
import json, sys
def must_load(path: str) -> object:
with open(path, encoding="utf-8") as fh:
try:
return json.load(fh)
except json.JSONDecodeError as exc:
sys.exit(f"{path}: line {exc.lineno} col {exc.colno}: {exc.msg}") # sys.exit is a real alias
All three of those exit codes were checked on a valid and an invalid file. Silent fallbacks — try: parse() except: pass — turn a five-second fix into a post-mortem.
FAQ
Is there a way to sort keys or keep comments? Not in this tool. No key-sorting control exists, and comments aren't preserved — a commented file is not JSON and the validator will say so at the exact column.
Why does the validator disagree with my Node script? Node tolerates leading/trailing whitespace; strict parsers follow RFC 8259 byte-for-byte. Trust the stricter one. See patterns and gotchas.
Paste 50 MB and the tab dies — why? Everything is in-browser, one thread, and the parse holds the whole document plus the formatted copy in memory. Split it or stream it; see the NDJSON guide for the streaming route.