Format guide
NDJSON vs JSON vs JSONL
JSON is one complete document. NDJSON is a sequence of complete JSON values, one per physical line. JSONL and JSON Lines usually mean the same line-delimited layout. Use JSON for a bounded payload; use NDJSON when records arrive, move, or fail one at a time.
Same records, different boundary
comma + brackets vs newline
[
{"host":"web-1","status":200},
{"host":"web-2","status":503},
{"host":"web-3","status":200}
]{"host":"web-1","status":200}
{"host":"web-2","status":503}
{"host":"web-3","status":200}The objects are ordinary JSON in both files. Only the outer framing changes. In NDJSON, the newline closes the record, so a record cannot be spread across several display lines.
Definitions and record boundaries
JSON: one value owns the whole document
A JSON document contains one top-level value: an object, array, string, number, boolean, or null. Whitespace around and inside that value is insignificant. A newline therefore has no special record meaning in ordinary JSON; it may simply be indentation inside one large array.
An array is the usual way to package several records. That is an excellent shape for a finite HTTP response or a file that will be read as a unit. It is less handy for a feed whose closing bracket may not arrive for hours.
NDJSON: newline-delimited JSON
NDJSON adds a framing rule to JSON: every non-empty line is one complete JSON value. A consumer can read a line, parse it, and release it without waiting for an outer array to close. Newlines inside a JSON string remain escaped as \n; they are not literal line breaks in the file.
Objects are the normal record shape, but the JSON Lines model permits any valid JSON value on a line. In practice, agree on one shape and schema. A stream that unexpectedly mixes objects, arrays, and scalars is valid syntax but awkward data.
JSONL and JSON Lines: another name, not another shape
In everyday tooling, JSONL, JSON Lines, and NDJSON describe the same useful contract: one JSON value per line. The common extensions are .jsonl and .ndjson. A particular API may prescribe one name or media type, so follow that API at the boundary; do not rewrite the bytes merely to change the label.
JSON or NDJSON: a working decision table
Choose based on how the data moves. The same record schema can travel in either container.
| Situation | Prefer | Why |
|---|---|---|
| A bounded API response | JSON | One complete payload is easy to validate and consume. |
| A live log or event feed | NDJSON / JSONL | A consumer can act whenever the next line arrives. |
| A large export processed record by record | NDJSON / JSONL | Line boundaries make sequential processing straightforward. |
| One deeply nested configuration object | JSON | The document is one value, not a sequence of independent records. |
| A small file people inspect by hand | Pretty JSON | Indentation helps more than line framing at this size. |
| Random lookup by record ID | Neither by itself | Add an index or use a database; a file extension does not create random access. |
The differences that matter in production
Latency and backpressure
An NDJSON producer can hand off a record as soon as that record is complete. The downstream process can then slow the producer through the pipe or socket instead of accepting an entire collection first. Framing alone does not guarantee low latency, though. User-space and pipe buffers still exist, so live producers need an explicit flush policy.
Memory
NDJSON makes bounded-memory code natural: read one line, parse one value, do the work, discard it. A specialized streaming JSON parser can also walk a large array incrementally, so JSON does not inherently require loading the whole file. Many everyday APIs and libraries do load a JSON document in one call, which is where the practical difference appears.
Failure recovery
A malformed NDJSON line has an obvious boundary. A reader may stop, quarantine that line, or report it and continue. The format does not choose the policy for you. With one JSON document, a syntax error can make the whole document invalid, even when most records look intact.
Appending, concatenating, and splitting
Appending a complete line to a newline-terminated NDJSON file preserves its structure. Concatenating two correctly newline-terminated NDJSON files does too. Two JSON arrays cannot be concatenated into a valid single document, and appending an item means editing the surrounding array. Line-based tools can also split NDJSON at record boundaries, provided records never contain physical newlines.
Schema evolution
Neither format supplies a schema. If fields change over time, put a version in each record or version the stream contract outside the file. Per-record framing makes mixed versions possible, but it does not make them safe automatically.
Produce NDJSON from command output with jc-rs
jc-rs streaming parsers have names ending in -s. They consume input line by line and, by default, write one compact JSON record per line. The -u option means “unbuffer”: it flushes stdout after every emitted record, which matters for a pipe that stays open.
tail -f /var/log/nginx/access.log \
| jc-rs -u --clf-s \
| jq -c 'select(.status >= 500)'Each part has a separate job. --clf-s recognizes Common or Combined Log Format, -u makes records visible immediately, and jq -c filters while keeping every result on one line. Leaving off -u does not change the NDJSON shape; it only allows buffered writes.
Important distinction
-u does not turn a batch parser into a streaming parser. Pick a streaming parser such as --syslog-s, --git-log-s, or --clf-s, then add -u when the consumer must see each record immediately.
A finite file does not need per-record flushing
cat access.log | jc-rs --clf-s > access.ndjsonThe output remains one JSON object per line. Letting jc-rs buffer writes avoids a flush for every record and is the sensible default when the input will close.
Keep going after a bad streaming record
ls -l | jc-rs -qq --ls-s > checked.ndjson
jq -c 'select(._jc_meta.success == false)' checked.ndjsonBy default, a streaming parse error stops the run. With -qq, jc-rs continues and adds _jc_meta.success to emitted records. A failed line becomes its own error record with the original line and error text, so the gap is visible rather than silently dropped. This is parser-specific: parsers that deliberately preserve unknown input in an unparsable field still consider that record successfully handled. Check the streaming parser's schema before routing failures.
Convert JSON to JSONL, and JSONL back to JSON
If the source JSON is a top-level array, jq can write each element as one compact line:
jq -c '.[]' records.json > records.jsonlThe reverse operation slurps every JSON value from the input stream into an array:
jq -s '.' records.jsonl > records.jsonjq -s holds the collected values in memory. That is fine for a bounded file that fits comfortably, but it removes the memory advantage of record-at-a-time processing. If the next system accepts NDJSON, keep the stream line-delimited instead of building an array only to split it again.
Four easy ways to break a JSON Lines pipeline
- Pretty-printing the stream. Multi-line indentation destroys the physical line boundary. Keep the transport compact and pretty-print only the record you are inspecting.
- Writing logs to stdout beside JSON. A progress message becomes a malformed record. Send diagnostics to stderr and reserve stdout for data.
- Forgetting the final newline. Many readers accept the last line without one, but a trailing newline makes safe concatenation and shell processing less surprising.
- Assuming framing is validation. One line can still contain invalid JSON or a valid value with the wrong fields. Validate syntax and schema at the boundary that owns the contract.
Common questions
Is an NDJSON file valid JSON?
Each record is valid JSON. A file containing two or more records is not one JSON document because it has several top-level values without an enclosing array.
What is the difference between NDJSON and JSONL?
Usually only the name and file extension. Both are commonly used for one complete JSON value per line. Confirm the exact contract when an API specifies a media type, encoding, or final-newline rule.
Can JSON Lines contain arrays or primitive values?
Yes. Every line may hold any valid JSON value. Objects are the safest convention for record streams because fields can evolve without relying on array position.
Should I use NDJSON for configuration files?
Usually not. Configuration is normally one nested document, so JSON, YAML, or TOML communicates that shape better. See the JSON, YAML, TOML, and XML comparison.
Choose the framing
Match the format to the consumer
Send JSON when the consumer needs the collection as one value. Send NDJSON when it must handle each record as it arrives, and make the producer's flush behavior explicit. jc-rs supplies line-oriented parsers for logs and long-running command output while leaving downstream tools with ordinary JSON fields.