Practical guide

Convert a JSON array to NDJSON or JSONL

NDJSON and JSONL describe the same everyday file shape: one complete JSON value on each line. Log processors, streaming jobs, and command-line tools can then consume records one by one.

You cannot make a trustworthy line-delimited file by deleting the array brackets and hoping the original whitespace cooperates. Parse every nonempty line on its own; each one must be a valid JSON value.

Array input, record lines out

The usual input is a JSON array. Each array item becomes one output line. Items can be objects, arrays, strings, numbers, booleans, or nulls, but the receiving pipeline may expect only object records.

The file extension can be .ndjson or .jsonl. Choose the extension your downstream tool expects; both names refer to the same one-value-per-line handoff pattern.

Fulfillment-event handoff that searches a 700-record batch, exports one compact JSON value per physical line, parses every saved line independently, and partitions records across six consumers.

Copyable line-delimited output

This compact example starts from a root object and selects .events before export. The shaped array has three items, so the NDJSON output has three physical lines. The string newline in the second record remains escaped as \n inside the JSON string; it does not become a physical record break.

Source JSON with root wrapperjson
{
  "events": [
    {"id":1,"message":"created"},
    {"id":2,"message":"line one\nline two"},
    {"id":3,"tags":["api","export"]}
  ]
}
JQ query for the recordsjq
.events
Shaped array to exportjson
[
  {"id":1,"message":"created"},
  {"id":2,"message":"line one\nline two"},
  {"id":3,"tags":["api","export"]}
]
NDJSON / JSONL outputndjson
{"id":1,"message":"created"}
{"id":2,"message":"line one\nline two"}
{"id":3,"tags":["api","export"]}

Export records

  1. Open the JSON source locally.
  2. Select or query the array that represents the records you want, such as .events for the example above.
  3. Choose Export, then NDJSON or JSONL if the UI labels both names.
  4. Save the file with the extension your receiver expects.
  5. Validate each nonempty line with a JSON parser and compare the line count with the selected input record count.

Validate line by line

A quick validation check reads the file as text, splits physical lines, skips the final empty line if present, and parses each line independently. If a line contains half an object because source pretty-printing leaked into the output, the file is not valid NDJSON.

This matters most when the original source is pretty-printed or uses JSONC-style conveniences. A physical newline inside the source file is not automatically a record boundary, and a trailing comma that a JSONC-aware view can tolerate still has to be removed or normalized before a strict NDJSON receiver can parse the line.

For a real handoff, validate the saved file with the same strictness as the receiver. If any nonempty line fails JSON.parse, fix the source shape or export path before sending it downstream.

Validation ideajs
const lines = text.split(/\r?\n/).filter(Boolean);
for (const line of lines) JSON.parse(line);
console.log(`${lines.length} records parsed`);

Source formatting is not the record model

A pretty JSON array may put one object across many lines. Those source line breaks are for humans, not record boundaries. The exporter should serialize each array item as one complete JSON value on one output line.

If the source is already JSONL or NDJSON and you want to open it, use How to open JSONL and NDJSON files. If you want to transform an array before export, shape it with JQ examples first.

For downstream tools, also decide whether all records should be objects. NDJSON technically permits any JSON value per line, but many importers expect object records with consistent keys. A file containing arrays, booleans, or null lines may be valid line-delimited JSON and still be rejected by the receiver.