JSONL to CSV Converter
JSONL to CSV converter. Flatten nested objects with dot.notation and export a CSV ready for Excel, Google Sheets or BigQuery. Up to 1 GB, runs in your browser, nothing uploaded.
100% client-side. Your file never leaves your browser. No upload.
Convert
JSONL to CSV Converter
Flatten nested JSONL / NDJSON into a flat CSV table. Columns are inferred automatically using dot.notation for nested keys. Open the output in Excel, Google Sheets, BigQuery, or pandas.
What this tool does
It reads JSONL (or NDJSON) — one JSON object per line — and rewrites it as a CSV table. Nested objects flatten into dot.notation columns, arrays spread across indexed columns (tags.0, tags.1), and the result is RFC 4180-compliant CSV you can hand to anything that reads spreadsheets. The conversion runs entirely in your browser; nothing is uploaded.
The intent this tool closes is the everyday one: "I have a JSONL file and I need it as a flat table I can open somewhere else." That "somewhere else" is usually Excel, Google Sheets, BigQuery, Snowflake, Athena, DuckDB, or a pandas DataFrame — all of which want flat columns, not nested JSON.
When you'd reach for it
- Open API or app logs in Excel. Pick comma delimiter, BOM Yes, CRLF — double-click the .csv and Excel renders it correctly, accents and all.
- Load into a data warehouse. Pick comma, LF, BOM No, Header Yes. BigQuery, Snowflake, and Athena autodetect a schema from the header row.
- Send a non-engineer a table. Paste the output straight into Google Sheets, or download and share.
- Feed pandas or DuckDB. Defaults work:
pd.read_csv("data.csv")orSELECT * FROM read_csv_auto('data.csv'). Flattened keys become column names. - Diff two exports. Convert both, compare in any spreadsheet or with plain
diff. - Strip nesting before ingest. Many pipelines prefer flat columns over JSON columns. This is the one-click way to get there.
How the conversion works
Three stages run on every click of Convert.
1. Parse, line by line
Each non-empty line is parsed as a standalone JSON value. Blank lines are silently skipped. If a line isn't a JSON object — for example a bare string, number, or top-level array — it's listed under Errors and dropped, because there are no keys to map to column headers. The rest of the file still converts; one bad line doesn't abort the whole run.
2. Flatten each record
Each object is walked recursively. Every leaf value becomes one entry in a flat key→value map; the separator (dot, underscore, or slash) joins the path parts together.
{"a": 1} → a
{"a": {"b": 1}} → a.b
{"tags": ["x", "y"]} → tags.0, tags.1
{"a": {}} → a (empty cell)
If you switch Nested to JSON-encode cell, the walker stops at the first nested level — any sub-object or sub-array is written as a single JSON string in one cell. Use that when you'd rather preserve structure than explode it across many columns.
3. Build the header, then emit rows
The CSV header is the union of every column key seen across every record, listed in the order keys first appear. That's why the converter has to read all input before writing any output: the column set for row 1 isn't known until the last row has been parsed. Rows are then emitted in column order. Missing keys become empty cells. Each value is escaped per RFC 4180: a cell containing the delimiter, the quote character, or a line break gets wrapped in quotes, and any embedded quote is doubled.
Options reference
Delimiter
Character between columns. Comma for standard CSV. Semicolon for European Excel locales that interpret comma as a decimal mark. Tab when your values are full of commas — tabs almost never collide with real data, so quoting stays minimal. Pipe for ad-hoc scripts and old ETL tools.
Quote
The wrapping character used only when a value needs escaping. Stick with double-quote; every spreadsheet, every warehouse loader, and pandas expects it. Single-quote is there for the rare downstream parser that demands it.
Header
Yes writes a first row with the column names. No writes data only — useful when you're appending to a table that already has a header.
Nested
Flatten recursively expands every nested object and array into its own column. JSON-encode cell keeps each nested value as a single JSON string in one column — much friendlier when records have unpredictable nesting or large variable-length arrays.
Separator
The character joining flattened key parts. Dot is conventional. Underscore is friendlier if you'll run SQL against the result, since column names like user_id don't need backtick quoting. Slash is useful when your original keys themselves contain dots.
EOL
Line terminator. LF for Unix tools, pandas, DuckDB, BigQuery, Snowflake. CRLF for legacy Windows Excel and Power Query flows that complain about LF-only files.
BOM
Prepends a UTF-8 byte-order mark (U+FEFF). Turn it on if you'll open the file in Excel and your data has non-ASCII characters — without the BOM, Excel often decodes the file as Windows-1252 and turns "café" into "café". Turn it off for everything else; BigQuery, Snowflake, pandas, and DuckDB prefer raw UTF-8.
Output format
- Encoding: UTF-8, optionally with a leading BOM.
- Quoting: RFC 4180. Cells are quoted only when they contain the delimiter, the quote character, or a line break. Embedded quotes are escaped by doubling them (
"→""). - Nulls and missing fields: both become an empty cell. CSV has no way to distinguish a JSON
nullfrom a missing key. - Empty objects and arrays:
{}and[]become a single empty cell at the path where they appeared. - Download: file is named
data.csvwith MIME typetext/csv.
Example
Input (JSONL):
{"id": 1, "user": {"name": "Ada"}, "tags": ["admin", "dev"]}
{"id": 2, "user": {"name": "Linus"}, "notes": "Active"}
Output (CSV, defaults):
id,user.name,tags.0,tags.1,notes
1,Ada,admin,dev,
2,Linus,,,Active
Header is the union of keys across both rows in first-seen order. Row 2 has no tags, so those cells are empty. Row 1 has no notes, so that cell is empty.
Recipes by intent
Open in Excel without mojibake
Delimiter comma, EOL CRLF, BOM Yes. Save the file, double-click. Cyrillic, accented Latin, and CJK characters all survive.
Load into BigQuery / Snowflake / Athena
Delimiter comma (or tab — both work; tab survives messy text better), EOL LF, BOM No, Header Yes. Upload, let the warehouse autodetect schema from the header row.
Read with pandas or DuckDB
Defaults are fine. Nested Flatten, dot separator, comma delimiter, LF, no BOM. Column names mirror your dot-paths exactly, so df["user.name"] and SELECT "user.name" both work.
Keep nested structure intact
If your records contain unpredictable nested arrays — chat messages, tag lists, event payloads — set Nested to JSON-encode cell. You'll get one column per top-level key, with the nested value stored as a JSON string the downstream code can parse on demand.
Round-trip without losing fidelity
Pick Tab delimiter and Nested JSON-encode cell. Tabs almost never appear inside values, and JSON-encoded cells preserve the full original structure — you can reverse the transformation later if you need to get back to JSONL.
SQL-safe column names
Set Separator to underscore. Flattened keys become user_name instead of user.name, so you don't need to quote them in SELECT statements.
Limits and performance
- Memory-bound, not file-bound. Input, parsed records, and output all sit in browser memory at once. On a modern desktop, ~500 MB inputs usually finish; on a phone or low-RAM laptop, treat ~100 MB as the ceiling.
- The header pass is unavoidable. Because the column union depends on every row, the converter can't stream output incrementally — it scans all input first, then emits. For multi-gigabyte files, reach for a CLI like
mlr --ijsonl --ocsv flatten then unsparsifyinstead. - Excel caps at 16,384 columns. Highly variable schemas or large arrays can blow past that. Switch to JSON-encode cell to collapse the explosion back into a manageable number of columns.
- The output textarea is the slow bit. Around 50–100 MB of rendered CSV, the right-hand pane may freeze even though the conversion finished. Use Download rather than Copy for large outputs.
Errors and how to fix them
Line N: invalid JSON
The line isn't parseable. Common causes: a trailing comma in an object, single-quoted keys, JavaScript-style comments, or a record split across multiple lines. Drop the file into the JSONL Validator to locate every bad line in one pass.
Line N: expected JSON object, got string / number / array
CSV is a table of records, so the top level of each line must be an object. If your input is NDJSON of arrays, wrap each in a small object first — for example {"row": [1, 2, 3]} — or use Nested JSON-encode cell after wrapping.
Excel shows everything in one column
You opened a comma-CSV in a European Excel locale that expects semicolon. Re-export with Delimiter: semicolon, or in Excel use Data → From Text/CSV and pick the delimiter manually.
Non-ASCII characters look corrupted
Re-export with BOM: Yes. Excel uses the BOM to recognize UTF-8; without it, older versions fall back to the local code page.
Column order isn't what I expected
Columns appear in first-seen order. If keys you care about live in later rows, they'll show up further right. Reorder in the spreadsheet after import, or move a "canonical" record to the top of your JSONL so all keys appear early.
The browser tab freezes during a big conversion
The conversion itself is usually fine; what freezes is rendering the result into a textarea. Use Download directly after Convert — the file is written without rendering the whole string in the DOM.
FAQ
Does my data get uploaded anywhere?
No. The script runs in your browser. You can disconnect from the network and it still works. Safe for API logs, customer data, and anything else you don't want crossing a server.
Will null values round-trip?
No. CSV has no concept of null; both null and a missing key become empty cells. If you need to preserve the difference, use JSON-encode cell mode, or convert via JSONL → JSON and ingest the array directly.
Why dot-notation instead of separate child tables?
Because the destinations people actually use — Excel, Sheets, BigQuery, pandas — want a single flat table. Splitting one JSONL into multiple normalized CSVs is a different job, usually done in code rather than a one-shot converter.
I have a JSON array file, not JSONL — can I convert it here?
Convert it to JSONL first with JSONL ↔ JSON, then paste the result here. The error messages on this tool are tuned for line-delimited input, which is why it doesn't accept arrays directly.
How many records and columns am I getting?
The status bar under the widget shows both: "Converted N rows across M columns", plus a count of any skipped lines. Useful for sanity-checking before you import somewhere expensive.