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

payload.jsonjson
{
  "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, and null are lowercase literal values.

  • Numbers are unquoted; NaN and Infinity are 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

directory containing payload.jsonbash
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

CI or local shellbash
if jq empty payload.json >/dev/null; then
  echo "JSON syntax is valid"
else
  echo "Invalid JSON" >&2
  exit 1
fi
JSON syntax is valid

Exit status is the contract

  • empty parses 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, limit retries, or verify region names.

  • Keep stderr visible in CI so a parser location reaches the build log.

Minify with jq

directory containing payload.jsonbash
jq --compact-output . payload.json > payload.min.json
wc -c payload.json payload.min.json
126 payload.json
100 payload.min.json
226 total

Compact 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

directory containing payload.jsonbash
python3 -m json.tool payload.json
python3 -m json.tool --compact payload.json > payload.min.json
{
    "service": "payments",
    ...
}

A useful zero-install fallback

  • json.tool uses 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 with python3 -m json.tool --help on 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

format-json.mjsjavascript
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.parse rejects 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_INTEGER can 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

directory containing payload.jsonbash
jq -r '.service' payload.json
jq -e '.enabled == true and (.retries | type == "number")' payload.json
payments
true

Structured queries understand types

  • -r writes a selected string without JSON quote characters.

  • .service addresses an object member; missing members yield null unless the filter enforces otherwise.

  • -e maps the last result to a useful exit status: false/null fail, true/other values succeed.

  • The type check distinguishes numeric 3 from string "3".

  • For a durable contract, move growing assertions into JSON Schema and test fixtures.

Syntax validation is not data validation

syntactically-valid-but-wrong.jsonjson
{
  "service": 42,
  "enabled": "yes",
  "retries": -100,
  "regions": []
}

A parser accepts this document

  • Every token follows JSON grammar.

  • The application may require service to be a non-empty string.

  • A boolean should be true or false, 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

service.schema.jsonjson
{
  "$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

  • $schema declares the JSON Schema dialect used by the document.

  • required controls presence; defining a property alone does not require it.

  • integer, bounds, and minLength constrain values beyond syntax.

  • uniqueItems rejects duplicate array values according to schema equality rules.

  • additionalProperties: false rejects 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 0 or a nonzero-leading integer; JSON does not permit 007.

  • undefined, comments, NaN, or Infinity → choose a valid JSON representation or a different data format.

Duplicate object names are a trap

ambiguous.jsonjson
{ "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.