A wall of one-line JSON is irritating. A payload that parses cleanly but carries the wrong types is dangerous. Those are different problems, and a formatter solves only the first. This reference moves from fast local formatting to syntax validation, structural queries, schema validation, and the edge cases that quietly break production integrations.
Pick the operation you actually need
Format or pretty-print → add whitespace and indentation without changing data values.
Validate syntax → prove the input conforms to a JSON parser’s grammar.
Lint → report syntax/style problems, often with a line and column.
Minify or compact → remove insignificant whitespace for transport/storage.
Query or transform → select, filter, map, or reshape values.
Schema validation → enforce required properties, types, formats, ranges, and relationships beyond JSON syntax.
A valid JSON example
{
"service": "payments",
"enabled": true,
"retries": 3,
"regions": ["ap-south-1", "eu-west-1"],
"owner": null
}The small syntax rules matter
Object member names and string values use double quotes; single-quoted strings are not JSON.
Object members use a colon, while members and array elements use commas.
true,false, andnullare lowercase literal values.Numbers are unquoted;
NaNandInfinityare not permitted by RFC 8259.A JSON text can be any serialized JSON value, although object/array roots remain most interoperable for APIs.
Format and validate with jq
jq . payload.json
jq --sort-keys . payload.json{
"enabled": true,
"owner": null,
"regions": [
"ap-south-1",
"eu-west-1"
],
"retries": 3,
"service": "payments"
}jq parses before it prints
.is the identity filter: it emits the parsed input value.Invalid syntax makes jq report an error and return a non-zero exit status.
--sort-keys(-S) makes object key order deterministic for review; it changes textual order, not object meaning.Array order is preserved because array position is meaningful.
Pretty-printing reserializes data, so whitespace and numeric spelling may change even when values appear equivalent.
Validate quietly in a script
if jq empty payload.json >/dev/null; then
echo "JSON syntax is valid"
else
echo "Invalid JSON" >&2
exit 1
fiJSON syntax is validExit status is the contract
emptyparses the input but intentionally emits no JSON value.Standard output is discarded because the script cares about success/failure.
The shell checks jq’s process status rather than grepping human-readable error text.
This validates syntax only; it does not require
service, limitretries, or verify region names.Keep stderr visible in CI so a parser location reaches the build log.
Minify with jq
jq --compact-output . payload.json > payload.min.json
wc -c payload.json payload.min.json126 payload.json
100 payload.min.json
226 totalCompact does not mean compressed
--compact-output(-c) emits each JSON value on one line.The shell writes to a new file, preserving the original for comparison.
Minification removes insignificant whitespace but does not use gzip/Brotli or reduce long keys/values.
HTTP content encoding usually saves more bandwidth than minification alone.
Never redirect output onto the input file; the shell truncates it before jq can read it.
Format JSON with Python’s standard library
python3 -m json.tool payload.json
python3 -m json.tool --compact payload.json > payload.min.json{
"service": "payments",
...
}A useful zero-install fallback
json.tooluses Python’s JSON parser and returns a non-zero status for invalid input.Default output is indented; current Python versions expose
--compact, but confirm options withpython3 -m json.tool --helpon the deployed version.Python’s parser behavior for duplicate names and large/nonnative numeric values may differ from downstream systems.
The module validates JSON grammar, not an application schema.
Specify a known Python runtime in CI so formatting does not drift across machines.
Format inside Node.js
import { readFile, writeFile } from 'node:fs/promises';
const [input, output] = process.argv.slice(2);
if (!input || !output) {
throw new Error('Usage: node format-json.mjs INPUT OUTPUT');
}
const text = await readFile(input, 'utf8');
const value = JSON.parse(text);
await writeFile(output, JSON.stringify(value, null, 2) + '
', 'utf8');Parsing and serialization are explicit
JSON.parserejects malformed JSON before the output file is written.JSON.stringify(value, null, 2)chooses two-space indentation.A final newline keeps the generated text friendly to POSIX tools and version control.
JavaScript numbers use IEEE-754 binary64; integers above
Number.MAX_SAFE_INTEGERcan lose precision when parsed.Duplicate object names are not safely preserved as separate members; avoid them at the producer boundary.
Query fields without fragile text matching
jq -r '.service' payload.json
jq -e '.enabled == true and (.retries | type == "number")' payload.jsonpayments
trueStructured queries understand types
-rwrites a selected string without JSON quote characters..serviceaddresses an object member; missing members yieldnullunless the filter enforces otherwise.-emaps the last result to a useful exit status: false/null fail, true/other values succeed.The type check distinguishes numeric
3from string"3".For a durable contract, move growing assertions into JSON Schema and test fixtures.
Syntax validation is not data validation
{
"service": 42,
"enabled": "yes",
"retries": -100,
"regions": []
}A parser accepts this document
Every token follows JSON grammar.
The application may require
serviceto be a non-empty string.A boolean should be
trueorfalse, not a human-oriented string.Retry limits may require an integer within a bounded range.
A deployment contract may require at least one supported region.
Express the contract with JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["service", "enabled", "retries", "regions"],
"properties": {
"service": { "type": "string", "minLength": 1 },
"enabled": { "type": "boolean" },
"retries": { "type": "integer", "minimum": 0, "maximum": 10 },
"regions": {
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 },
"uniqueItems": true
},
"owner": { "type": ["string", "null"] }
},
"additionalProperties": false
}A schema makes expectations reviewable
$schemadeclares the JSON Schema dialect used by the document.requiredcontrols presence; defining a property alone does not require it.integer, bounds, andminLengthconstrain values beyond syntax.uniqueItemsrejects duplicate array values according to schema equality rules.additionalProperties: falserejects unknown object members; use it deliberately because it affects forward compatibility.
Common JSON errors and precise fixes
Unexpected token after a value → remove the trailing comma or add a missing comma between members.
Property names must be double-quoted → replace JavaScript object-literal syntax with JSON strings.
Unexpected end of input → close the current string, object, or array and check truncation.
Invalid escape → use JSON escapes such as
\n,\t,\",\\, or a valid Unicode escape.Leading zero in a number → write
0or a nonzero-leading integer; JSON does not permit007.undefined, comments,NaN, orInfinity→ choose a valid JSON representation or a different data format.
Duplicate object names are a trap
{ "role": "reader", "role": "admin" }Interoperability fails even when one parser accepts it
RFC 8259 says object names should be unique for interoperable behavior.
Libraries differ: some keep the last value, some keep the first, some expose all pairs, and strict modes may reject duplicates.
Formatting through a typical object model can silently discard one value.
Reject duplicates at ingestion when security or deterministic signing matters.
Never use duplicate names as an ordered multimap; represent repeated entries as an array.
Large integers and decimal precision
JSON defines decimal number syntax but does not guarantee a universal runtime precision.
JavaScript cannot exactly represent every integer above 9,007,199,254,740,991 as a Number.
Transmit database IDs, account numbers, and cryptographic quantities as strings when arithmetic is not required.
Use a decimal/big-integer-aware parser when exact arithmetic is required.
Validate lexical and range rules at system boundaries; a formatter cannot recover digits already rounded upstream.
JSON, JSON5, JSONC, and JavaScript objects
JSON uses double-quoted names/strings and forbids comments/trailing commas.
JSONC commonly means JSON with comments; support is tool-specific.
JSON5 permits conveniences such as comments and more JavaScript-like syntax, but it is not RFC 8259 JSON.
A JavaScript object literal may contain functions,
undefined, computed keys, and other values JSON cannot represent.Name the accepted format explicitly in file extensions, APIs, schemas, and documentation.
When an online editor is reasonable
The payload is synthetic or public and contains no credentials, personal data, proprietary source, or internal topology.
The site’s privacy policy, retention, client-side/server-side processing, telemetry, and third-party scripts are acceptable.
The tool runs over HTTPS and the organization permits its use.
You need an interactive tree view, folding, search, diff, or one-off teaching aid.
You still verify important results with the parser/schema used by the actual application.
When to stay offline
Production logs, support bundles, incident payloads, cookies, JWTs, API keys, webhook signatures, customer records, health data, or payment information are present.
The document is covered by a confidentiality agreement, security policy, residency rule, or regulated-data requirement.
The payload is extremely large or untrusted and could exhaust a browser tab.
Exact parser behavior, duplicate detection, number precision, or schema dialect must match production.
The workflow belongs in CI and needs reproducible versioned tooling.
A dependable team workflow
Keep representative redacted fixtures in version control.
Run syntax and schema validation in pre-commit/CI using pinned tool versions.
Format generated JSON deterministically, but avoid noisy key reordering when order carries human review context.
Set parser limits for depth, size, strings, arrays, and numbers on untrusted inputs.
Return actionable JSON Pointer paths and schema keyword failures to API clients.
Fuzz and regression-test boundary cases, Unicode, duplicates, huge numbers, and malformed escapes.
Quick decision table
Readable local output →
jq . file.json.Deterministic key order for review →
jq -S . file.json.Compact one-line output →
jq -c . file.json.No jq available but Python installed →
python3 -m json.tool file.json.Application contract → a pinned JSON Schema validator for the declared dialect.
Sensitive payload → trusted offline tool only.
Transformation/filtering → jq or application code with tests, not search-and-replace.
Before sharing a failing payload
Replace credentials, cookies, tokens, signatures, personal data, and internal hostnames with type-preserving placeholders.
Keep the smallest document that still reproduces the parser or schema failure.
Record parser/library version, schema dialect, command, exit status, and exact error.
Confirm the redacted document still fails for the same reason before attaching it to a ticket.
Primary references
RFC 8259 defines JSON grammar, values, strings, numbers, UTF-8 interoperability guidance, duplicate-name concerns, and parser behavior.
The official jq manual documents compact output, sorted keys, raw output, filters, and exit-status behavior.
JSON Schema documents how schemas validate structure and constraints beyond JSON syntax.
Use the documentation for the exact parser and schema validator deployed in production; accepted extensions and numeric behavior vary.
Comments and corrections