JSON is a small specification with a lot of folklore attached to it. Knowing what the standard actually permits saves you from a surprising number of production bugs.
RFC 8259 defines six value types: object, array, string, number, boolean and null. Notably absent: comments, trailing commas, single-quoted strings, unquoted keys, hex numbers, NaN, Infinity, and dates. There is no date type — an ISO 8601 string is a convention, not a format feature. Keys should be unique; the spec says behaviour is undefined when they are not, and parsers differ, with most taking the last occurrence. Text must be UTF-8 for interchange, and the top-level value may be any type, not just an object or array.
Two-space indentation, keys in the order the producer emitted them, and a newline at end of file are conventions, not requirements. What matters practically is consistency, because inconsistent formatting turns every diff into noise. Sort keys when the file is committed to version control — jq -S . config.json makes diffs meaningful, since a serialiser that changes key order otherwise rewrites the whole file. For payloads on the wire, do the opposite and minify: whitespace is a measurable fraction of a large response body.
jq . # pretty-print
jq -S . # pretty-print, keys sorted
jq -c . # minify to one line
jq -r '.items[].name' # raw strings, no quotes
jq '.items | length' # count
jq '[.items[] | select(.active)] | map(.id)'
jq -r '.rows[] | [.id, .name, .email] | @csv'
jq -s 'add' a.json b.json # slurp multiple files into one array
Two flags do most of the work: -r when you are feeding output into another shell command, and -c when you want one object per line. jq empty file.json is the fastest validity check available — it prints nothing and exits non-zero on malformed input.
JSON numbers have no defined precision, and JavaScript parses them as IEEE 754 doubles. Integers above 2^53 lose precision silently: a 64-bit database ID like 9007199254740993 comes back as 9007199254740992. This is the single most common data-corruption bug in JSON APIs. The fix is to serialise large integers as strings and document it. The same class of problem hits money — never store currency amounts as floats; use integer minor units or a string.
One JSON object per line, no wrapping array, newline-delimited. It streams, it appends, it survives truncation, and you can process it with standard line tools: head -n 100 events.jsonl | jq -c 'select(.level=="error")'. A 4 GB JSON array must be fully parsed into memory; a 4 GB JSONL file processes in constant memory. Use .jsonl for logs, exports and event streams, and reserve the array form for small documents.
JSON5 and JSONC add comments and trailing commas and are fine for configuration files a human edits — tsconfig.json and VS Code settings are JSONC. They are not fine on the wire, because a standard parser rejects them. If you want comments in a config a machine also reads, either use JSONC with a parser that supports it, or switch that file to YAML or TOML and stop fighting the format.
\/ is legal but pointless.\u2028 or \u2029 in a string is valid JSON but historically broke when embedded in JavaScript source. Escape them if you inline JSON into a script tag, and escape </ as <\/ so a string cannot close the tag.