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.

.jsonl
Drop a file here
Stays in your browserUp to 1.5 GB

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.

  1. Drop the file on rows.page or choose it from disk.
  2. 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:00Z are read as timestamps and written back as 2026-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

Ways to convert JSONL to a JSON array
MethodKeeps nestingCommand or steps
rows.pageNo: objects become dot-path keys, lists stay arraysDrop the file, then Export > JSON
jqYesjq -s '.' data.jsonl > data.json
PythonYesParse each non-empty line with json.loads, collect a list, write it with json.dump
DuckDBYes, but keys missing from a record come back as nullCOPY (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.

bash
jq -s '.' data.jsonl > data.json

Add -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:

python
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:

python
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:

sql
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

WhatLimit or price
Open a file in your browserFiles 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.