JSONL to JSON
How to convert JSONL to a JSON array
rows.page converts a JSONL file up to 1.5 GB into one JSON array in 2 steps, free with no signup: drop the file, then choose JSON in the Export menu. Nested objects come out as dot-path keys, so use jq -s or Python when you need the original nesting.
No file handy? Open GSM8K math eval, test split (1,319 rows, 750 KB).
Convert JSONL to a JSON array in rows.page
rows.page opens .jsonl and .ndjson files, or JSONL lines you paste with Paste data. DuckDB-WASM reads them inside your browser tab, so nothing is uploaded unless you press Share.
- Drop the file on rows.page or choose it from disk.
- Open the Export menu and choose JSON to download one array of objects.
The export is a flat table, not a copy of your original lines. Expect these changes:
{"user": {"city": "NY"}}comes out as{"user.city": "NY"}: nested objects become dot-path keys, up to 8 levels deep and 200 fields per object.- Lists stay real arrays, objects inside them included.
- A whole number in a column that also holds decimals comes back as
7.0. - Date-time strings such as
2026-01-03T10:00:00Zare read as timestamps and written back as2026-01-03 10:00:00.
If your code expects the original records, use jq or Python instead. A JSONL file over 1.5 GB opens only its first rows, with a note saying how many were loaded, so its export is partial.
Compare the ways to convert JSONL to JSON
| Method | Keeps nesting | Command or steps |
|---|---|---|
| rows.page | No: objects become dot-path keys, lists stay arrays | Drop the file, then Export > JSON |
| jq | Yes | jq -s '.' data.jsonl > data.json |
| Python | Yes | Parse each non-empty line with json.loads, collect a list, write it with json.dump |
| DuckDB | Yes, but keys missing from a record come back as null | COPY (SELECT * FROM read_json('data.jsonl')) TO 'data.json' (FORMAT json, ARRAY true); |
Filter or run SQL before you convert
rows.page exports the current view, so you can trim the data first. Filter with country:NL, price:>20, name:* (has a value) or -name:* (is empty), or click histogram bars in the Columns panel; a filtered view exports every matching row.
For SQL, type a statement that starts with SELECT, WITH or FROM against the table data. Quote dot-path names, as in "user.city", and add ORDER BY __rid to keep the file order. SQL results export up to 1,000,000 rows.
Convert JSONL to JSON with jq
The -s (slurp) flag reads every line and wraps the objects in one array, nesting untouched.
jq -s '.' data.jsonl > data.jsonAdd -c for compact output without indentation: jq -cs '.' data.jsonl > data.json. jq holds the whole array in memory, so watch your RAM on multi-gigabyte files. If a line isn't valid JSON, jq stops with a parse error and data.json stays empty.
To check the result, compare jq length data.json with grep -c . data.jsonl, which counts the non-empty lines. The two numbers should match.
Convert JSONL to JSON with Python
Python's built-in json module needs no install. This skips blank lines and keeps every nested object as it was:
import json
with open("data.jsonl", "r", encoding="utf-8") as infile:
records = [json.loads(line) for line in infile if line.strip()]
with open("data.json", "w", encoding="utf-8") as outfile:
json.dump(records, outfile)By default json.dump escapes characters like é as \u00e9; pass ensure_ascii=False to keep them as they are, and indent=2 to pretty-print. Like jq -s, this keeps the whole list in memory.
For a file bigger than your RAM, write the array one record at a time. Each line is still parsed, so a broken line stops the script with a JSONDecodeError:
import json
with open("data.jsonl", encoding="utf-8") as infile, open(
"data.json", "w", encoding="utf-8"
) as outfile:
outfile.write("[\n")
records = (line for line in infile if line.strip())
for index, line in enumerate(records):
if index:
outfile.write(",\n")
json.dump(json.loads(line), outfile)
outfile.write("\n]\n")Convert JSONL to JSON with DuckDB
DuckDB keeps nested objects as objects and writes the whole array in one statement:
COPY (SELECT * FROM read_json('data.jsonl')) TO 'data.json' (FORMAT json, ARRAY true);It infers one schema for all lines, so a key missing from a record comes back as null. The number and date changes listed above apply here too, because rows.page runs the same engine in your browser.
When to keep the file as JSONL
Before you convert, check whether the tool that receives the file reads JSONL directly. OpenAI's Batch API takes JSONL input files, and BigQuery loads JSON data only as newline-delimited JSON. For large datasets, keep JSONL or export zstd Parquet from rows.page instead.
rows.page limits and prices
| What | Limit or price |
|---|---|
| Open a file in your browser | Files up to 1.5 GB, $0, no account, nothing uploaded |
| Share a link without an account | $0, files up to 100 MB, link deleted 7 days after creation |
| Free account | $0, 10 datasets kept, last 3 versions |
| Pro | $12/month, 100 datasets, files up to 500 MB, private links |
| Max | $39/month, 1,000 datasets, files up to 2 GB, private links |
Questions
What is the difference between JSONL and NDJSON?
Nothing: both names describe text files with one JSON value per line. rows.page opens .jsonl and .ndjson files the same way.
Is my JSONL file uploaded when I convert it?
No, the conversion runs in your browser and needs no account. Only Share uploads the file; without an account one push can be up to 100 MB, and the link expires after 7 days.
How do I copy one record as nested JSON?
Click the row to open the side drawer and use Copy as JSON. It rebuilds the nested objects from the dot-path columns, but only for that one row.
Can I open a JSONL file from a URL?
Yes. Put the public URL after rows.page/, as in https://rows.page/https://example.com/data.jsonl. If the host blocks cross-origin requests, rows.page fetches the file through its proxy (up to 100 MB, 30 seconds, 5 redirects).
What happens to lines that aren't valid JSON?
rows.page still opens the file and shows the note "Some lines weren't valid JSON and were skipped." jq -s stops at the first broken line instead.