Skip to content
DevPenh

How to Format and Validate JSON (jq, Python, Node)

Pretty-print, minify and sort JSON, see how formatters change numbers, and validate JSON with tested jq, Python and Node.js commands, in code and in CI.

By Yorn Chanpenh · Last reviewed

Formatting and validating JSON are two different jobs that most tools do at once. Formatting changes only whitespace, so the document is easier to read or smaller to send. Validation checks that the text is JSON at all. A formatter has to parse the input first, so it can only format JSON that is valid.

The quickest way to do both is to paste the document into the JSON formatter, which runs in your browser and shows the line and column of the first error. The rest of this guide explains the options and shows how to do the same in the terminal, in code and in CI. The commands were run with jq 1.7.1, Python 3.9 and Node.js 24.

Pretty-print or minify

The same data can be written with indentation for people or without any whitespace for machines:

{
  "id": 1024,
  "name": "Ada Lovelace",
  "roles": [
    "admin",
    "editor"
  ],
  "settings": {
    "theme": "dark",
    "beta": false
  }
}

{"id":1024,"name":"Ada Lovelace","roles":["admin","editor"],"settings":{"theme":"dark","beta":false}}

For this small document, the size in bytes is:

StyleBytes
Minified101
Tabs132
2 spaces146
4 spaces174

Use 2 spaces for files people read and review, such as configuration and fixtures; it is the default in most tools. Minify JSON that machines send to each other. Over HTTP, gzip or Brotli compression removes much of the difference anyway, so minifying matters most for storage and for responses that are not compressed.

Sort keys for stable diffs

Object keys have no meaningful order in JSON, but different serializers write them in different orders. Sorting keys before you save or compare documents keeps diffs small and reviewable. Arrays must not be sorted, because their order is part of the data. The formatter's Sort keys A–Z option sorts object keys at every level and leaves arrays alone, and so do jq --sort-keys and python3 -m json.tool --sort-keys. To see what actually changed between two versions, the JSON diff tool ignores key order for you.

Formatting can change numbers

A formatter rewrites the document from the parsed data, so anything the parser does not keep is lost. Numbers are where this shows. The same input gives three different results:

Input:   {"score": 1.0, "big": 1e2, "id": 12345678901234567890}

jq:      {"score":1.0,"big":1E+2,"id":12345678901234567890}
Python:  {"score":1.0,"big":100.0,"id":12345678901234567890}
Node.js: {"score":1,"big":100,"id":12345678901234567000}

All of these are valid JSON, and for small numbers they mean the same value. The last one is not: JavaScript stores numbers as 64-bit floats, so integers above 9,007,199,254,740,991 are rounded. Browser-based formatters use JavaScript too; this site's formatter warns when the input contains such integers instead of changing them silently. If you control the API, send 64-bit IDs as strings.

Two other things change when JSON is re-serialized. Escapes such as \u00e9 may be written as the character itself, and when an object repeats a key, only the last value is kept: {"a":1,"a":2} becomes {"a":2} in jq, Python and Node.js alike.

What "valid JSON" means

JSON is defined by RFC 8259 and ECMA-404, and it is stricter than JavaScript. Trailing commas, single quotes, unquoted keys, comments, NaN and undefined are all errors. The common JSON syntax errors guide lists each one with its fix. Valid JSON can still be the wrong data, though: a missing field or a number sent as a string parses fine. Checking the structure is a separate step, covered below.

Format and validate in the terminal

# jq: pretty-print (default 2 spaces), sort keys, or minify
jq . data.json
jq --sort-keys --indent 4 . data.json
jq -c . data.json

# Python standard library (--indent needs Python 3.9 or later)
python3 -m json.tool --indent 2 data.json
python3 -m json.tool --sort-keys --compact data.json

# Node.js
node -e 'console.log(JSON.stringify(JSON.parse(require("fs").readFileSync(0, "utf8")), null, 2))' < data.json

All three fail with a non-zero exit code when the input is invalid, and print where the parser stopped. For {"name": "Ada", "active": true,}:

ToolMessage
jq 1.7.1jq: parse error: Expected another key-value pair at line 1, column 32
Python 3.9Expecting property name enclosed in double quotes: line 1 column 32 (char 31)
Node.js 24SyntaxError: Expected double-quoted property name in JSON at position 31 (line 1 column 32)

To validate without printing anything, use jq empty. It exits with 0 for valid input and a non-zero code otherwise.

Validate JSON in code

Parse inside a try block and report the error message, which already includes the position. In JavaScript or TypeScript:

function parseJsonSafely(text) {
  try {
    return { ok: true, value: JSON.parse(text) };
  } catch (error) {
    return { ok: false, error: error.message };
  }
}

In Python:

import json

try:
    data = json.loads(text)
except json.JSONDecodeError as e:
    print(f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}")

In Dart and Flutter, jsonDecode throws a FormatException; see Flutter JSON errors for that and for the type errors that come after a successful parse.

Validate JSON files in CI

Broken configuration files are cheap to catch before they are deployed. This shell loop stops at the first invalid file and names it:

for f in config/*.json; do
  jq empty "$f" || { echo "Invalid JSON: $f"; exit 1; }
done

To check structure as well as syntax, describe the expected shape in a JSON Schema and run a schema validator in the same step. A schema can require fields, restrict types and reject unknown keys, which catches the "valid but wrong" cases that a parser accepts.

Large files

A browser formatter keeps the whole document in memory several times over: the text, the parsed data and the formatted output. Files of a few megabytes are fine; this site's formatter warns above 5 MB. For hundreds of megabytes, use jq in a terminal. Logs and exports that contain one JSON object per line are JSON Lines, not a single JSON document; format them one line at a time, for example with jq -c . events.jsonl.

Part of the API debugging series. Start with How to Debug API Responses: A Practical Workflow.