BytePane

Pretty-Print JSON in JavaScript and Python: Indent, Sort Keys, CLI

JSON6 min read

Quick answer

JavaScript: JSON.stringify(obj, null, 2), the third argument is the indent (a number up to 10, or a string such as '\t'). Python: json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False). Shell: python3 -m json.tool --indent 2 --sort-keys file.json. JavaScript has no built-in key sorting; use a replacer function.

JavaScript: JSON.stringify(value, replacer, space)

MDN defines space as the number of spaces to indent, clamped to 10, or a string whose first 10 characters are inserted before every nested level. Output from Node 20:

const obj = { name: 'bytepane', tags: ['json', 'tools'], nested: { b: 2, a: 1 }, empty: {} }

JSON.stringify(obj, null, 2)
// {
//   "name": "bytepane",
//   "tags": [
//     "json",
//     "tools"
//   ],
//   "nested": {
//     "b": 2,
//     "a": 1
//   },
//   "empty": {}
// }

JSON.stringify({ a: [1] }, null, 20)   // indents by 10, not 20
// {
//           "a": [
//                     1
//           ]
// }

// replacer ARRAY = allowlist of property names (applies at every level)
JSON.stringify(obj, ['name', 'nested', 'a'], 2)
// {
//   "name": "bytepane",
//   "nested": {
//     "a": 1
//   }
// }

Sorted keys at every level

const sorted = (key, value) =>
  value && typeof value === 'object' && !Array.isArray(value)
    ? Object.fromEntries(Object.keys(value).sort().map(k => [k, value[k]]))
    : value

JSON.stringify(obj, sorted, 2)
// {
//   "empty": {},
//   "name": "bytepane",
//   "nested": {
//     "a": 1,
//     "b": 2
//   },
//   "tags": [
//     "json",
//     "tools"
//   ]
// }

The replacer function is called for every key/value pair, including the root (key ""), so rebuilding each plain object with sorted keys sorts the whole tree. Arrays keep their order.

Python: json.dumps(obj, indent=2, sort_keys=True)

import json

obj = {"name": "bytepane", "tags": ["json", "tools"], "nested": {"b": 2, "a": 1}, "empty": {}, "city": "São Paulo"}

print(json.dumps(obj, indent=2))
# ...
#   "empty": {},
#   "city": "S\u00e3o Paulo"        <- ensure_ascii=True is the default
# }

print(json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False))
# {
#   "city": "São Paulo",
#   "empty": {},
#   "name": "bytepane",
#   "nested": {
#     "a": 1,
#     "b": 2
#   },
#   "tags": [
#     "json",
#     "tools"
#   ]
# }

print(json.dumps({"a": 1, "b": [1, 2]}, separators=(",", ":")))   # {"a":1,"b":[1,2]}  compact

sort_keys=True sorts recursively. ensure_ascii=False writes UTF-8 characters directly, which matches what JavaScript does by default.

Command line, no extra tools

$ echo '{"b":1,"a":{"y":2,"x":1},"s":"São"}' | python3 -m json.tool --sort-keys --indent 2 --no-ensure-ascii
{
  "a": {
    "x": 1,
    "y": 2
  },
  "b": 1,
  "s": "São"
}

$ echo '{"b":1,"a":[1,2]}' | node -e "process.stdin.on('data', d => console.log(JSON.stringify(JSON.parse(d), null, 2)))"
{
  "b": 1,
  "a": [
    1,
    2
  ]
}

python3 -m json.tool also accepts --tab, --compact, and an output file argument; run it with --help for your Python version.

Reference table

TaskJavaScriptPython
Indent 2 spacesJSON.stringify(obj, null, 2)json.dumps(obj, indent=2)
Indent with tabsJSON.stringify(obj, null, '\t')json.dumps(obj, indent='\t')
Maximum indentClamped to 10 spaces / 10 charsNo limit
Sort keysReplacer function (see below)sort_keys=True
Keep only some keysReplacer array: ['name', 'id']Filter the dict first
Non-ASCII charactersKept as-is (UTF-8)Escaped (\u00e3) unless ensure_ascii=False
Compact outputJSON.stringify(obj)json.dumps(obj, separators=(',', ':'))
undefined / functionsOmitted in objects, null in arraysTypeError: not JSON serializable
NaN / InfinityWritten as nullWritten as NaN / Infinity (invalid JSON) unless allow_nan=False
BigInt / intBigInt throws TypeErrorint of any size is fine
Command linenode -e (see below)python3 -m json.tool --indent 2 --sort-keys

Values that silently change or throw

// JavaScript (Node 20)
JSON.stringify({ u: undefined, f() {}, s: Symbol('x'), n: NaN, i: Infinity, d: new Date(0), arr: [undefined, () => {}] })
// {"n":null,"i":null,"d":"1970-01-01T00:00:00.000Z","arr":[null,null]}
//  ^ u, f, s omitted; NaN/Infinity -> null; Date -> toISOString(); array holes -> null

JSON.stringify({ big: 10n })
// TypeError: Do not know how to serialize a BigInt
# Python 3.10
json.dumps({"n": float('nan'), "i": math.inf})
# '{"n": NaN, "i": Infinity}'      <- not valid JSON; JSON.parse('NaN') throws in JavaScript

json.dumps({"n": float('nan')}, allow_nan=False)
# ValueError: Out of range float values are not JSON compliant

Python's json.loads accepts NaN on the way back in, which is why Python-to-Python round trips hide the problem until a JavaScript client or a strict validator sees the payload.

Primary sources: MDN JSON.stringify(), Python json module, RFC 8259.

Frequently Asked Questions

How do I pretty-print JSON with 4 spaces instead of 2?

JavaScript: JSON.stringify(obj, null, 4). Python: json.dumps(obj, indent=4). Command line: python3 -m json.tool --indent 4 file.json. In JavaScript the space argument is clamped to 10, so JSON.stringify(obj, null, 20) still indents by 10 spaces.

How do I sort JSON keys alphabetically?

Python has it built in: json.dumps(obj, sort_keys=True) or python3 -m json.tool --sort-keys. JavaScript has no flag; pass a replacer function to JSON.stringify that rebuilds each plain object from Object.keys(value).sort(), which sorts every nesting level.

Why does JSON.stringify drop some of my properties?

Per MDN, undefined, functions and symbols are not valid JSON values: JSON.stringify omits them when they are object properties and writes null when they are array elements. NaN and Infinity become null. BigInt values throw a TypeError unless you provide a replacer or a BigInt.prototype.toJSON.

Why does Python output NaN in my JSON?

json.dumps defaults to allow_nan=True, which emits the JavaScript literals NaN, Infinity and -Infinity. They are not valid JSON per RFC 8259, and JSON.parse in JavaScript rejects them. Pass allow_nan=False to get a ValueError instead of invalid output, and clean the data before serializing.

Try it in the browser

  • JSON Formatter, pretty-print and validate JSON without leaving the browser.
  • JSON Minifier, the reverse operation for payloads and config bundles.

Related guides