Convert NDJSON to TOML

Converting NDJSON to TOML writes each line of your file as a [[items]] block — a TOML array of tables, which is the format’s way of holding a list of records. It suits a short file that is about to be committed and hand-edited, and it is the wrong destination for anything with thousands of lines in it.

  • Where it runs In your browser. The file is never uploaded.
  • Lossless Nothing is discarded. The TOML holds exactly what the NDJSON held.
  • File size limit Up to 100 MB per file, free, without an account.

Up to 100 files at once. Mixed formats are fine.

TOML has a shape for a list of records, and it is readable

People assume TOML cannot hold what JSON holds, and for a list of records that is wrong. An array of tables is written as the same header repeated in double brackets — [[items]] three times gives three records — with each block carrying its own keys underneath. It is the construct Cargo uses for dependencies and Hugo uses for menu entries.

Compared with the JSON it came from it is more verbose and considerably easier to edit. Adding a record means copying four lines rather than balancing brackets and remembering a comma, and a diff of a change to one record touches only that block. For a file a person maintains, that is the entire argument.

Every line becomes one block, and the key is called items

A TOML document has to be a table at the top level, and a newline-delimited file is a list with no name of its own. So the list gets a placeholder name: the output is a run of [[items]] blocks, one per line of the source.

That name is the first thing to change, and it is a search and replace over the block headers — to [[dependencies]], [[authors]], [[products]], whatever the tool reading the file expects. There is nothing in an NDJSON file that could tell the converter what the records are, so the placeholder is deliberate and deliberately obvious rather than a guess that might look plausible and be wrong.

Records that differ need no reconciliation here

This is the quiet advantage of TOML over every tabular target for the same file. A CSV, a Parquet file or a run of INSERT statements has to reconcile records into one set of columns, filling in blanks where a record had nothing. An array of tables does not: each block carries exactly the keys its line had.

So a file where later records gained a field converts with no nulls, no empty cells and no widening. The result reads as what it is — a list of records that are mostly alike — rather than as a sparse grid. For a fixture set assembled over time, that is usually the more honest representation.

Nulls vanish, and nothing warns you

TOML has no null literal, and the writer does not invent one. A key whose JSON value was null is simply not written into the block, at the top level of a record or anywhere inside it.

Whether that matters is a question about the tool reading the file. Many treat a missing key and an empty value identically, falling back to a default either way, and for those nothing was lost. Where the distinction drives behaviour, the conversion has quietly changed the data, and the cheapest defence is to search the source for null before converting rather than to compare the two files afterwards — an absent line is much harder to spot than a changed one.

Nested fields become dotted sub-tables

A record with a nested object produces a sub-table under its block: an author object inside a record becomes an [items.author] header with its keys beneath. Depth is expressed in the header rather than in indentation, so even three levels stay flat on the page.

It is legal at any depth and unpleasant past two or three. A header like [items.metadata.source.system] is valid and nobody enjoys reading it, and a fixture file that produces those is usually carrying more structure than a hand-edited file should. That is a signal about the data rather than about the conversion.

Scalars are hoisted above the first block

A TOML table header claims every line written after it until the next header, so a plain key placed below a block would silently become part of that block. The writer therefore puts any top-level scalar above the first header.

For this pair that rarely comes up, because the input is a list and everything ends up inside a block. It matters when you edit the file afterwards: adding a version key at the bottom of the file puts it inside the last record rather than at the top level, which is the single most common mistake people make with TOML. Add such keys at the top, above the first double-bracket header.

Timestamps stay strings, because the source had no date type

TOML 1.0 has four date and time types, including an offset date-time that is a real value rather than text. JSON has none — every timestamp in every JSON file is a string or a number by convention — so a value of "2024-01-01T00:00:00Z" is written as a quoted TOML string.

That is correct and it is not what the format could express. If the tool reading the file wants a real date-time, removing the quotation marks on those lines is the whole fix, and nothing else in the file has to change. It is a reasonable thing to do while you are in there renaming the block headers.

How many records is too many

A few dozen is comfortable. A few hundred is a file nobody will read but a tool will still parse quickly. A few thousand blocks is a configuration file in name only, and at that point every property that made TOML attractive — legible diffs, hand editing, comments next to values — has stopped applying.

The stopping rule is worth applying honestly: if nobody is going to open the file, do not convert it to a configuration format. CSV is smaller and every tool reads it, JSON is what the data already was, and a database is what you want if the list is going to be queried. TOML earns its place only when a person is going to maintain the result.

What the TOML file gains that the NDJSON could not have

Comments, which is the whole reason config formats look like this. JSON has none, and neither does a line-delimited file made of it, so the reasons behind the values in a fixture set currently live in a commit message or in nobody’s head at all.

Once the file is TOML, those reasons can sit next to the entries: why this record is excluded from a test, which upstream identifier a value has to match, what breaks if an entry is reordered. That is the information a generated file could never carry, and the first minutes after conversion are the cheapest time to write it down.

The conversion runs in the browser, like the rest of this group

Both halves are JavaScript in this tab: the file is parsed line by line and the TOML is written by a small library loaded on demand. Nothing is uploaded, there is no account, and the free tier accepts up to 100 MB — which for a file this conversion is appropriate for is not a limit anyone approaches.

The privacy point still applies even at this scale. A fixture set is often real data with the names left in, and a seed file for a development database is frequently a slice of production. Not sending it anywhere is a smaller decision here than on a log file, and it is still the right default.

How to turn NDJSON records into TOML tables

  1. Drop the .ndjson or .jsonl file onto this page — a short one, if it is going to be maintained by hand.
  2. Each line becomes an [[items]] block, in your browser.
  3. Rename items to what the tool expects, check for keys that held null, and add the comments the source could not carry.

NDJSON and TOML: a record stream as an array of tables

NDJSON compared with TOML
NDJSONTOML
Full nameNewline-Delimited JSONTom's Obvious Minimal Language
File extension.ndjson, .jsonl.toml
Media typeapplication/x-ndjsonapplication/toml
First published20132013
SpecificationTOML 1.0
LicensingOpen standardOpen standard
Standing todayCurrentCurrent
Opens in a browserNo browserNo browser
Considered insteadJSON, CSVYAML, JSON, INI

What survives

Nothing is discarded. NDJSON and TOML both store their content losslessly, so the conversion is a change of packaging rather than a change of quality, and it can be repeated without accumulating damage.

Opening the result

The usual programs do not overlap: NDJSON opens in jq and pandas, TOML in Visual Studio Code — so whoever receives the result needs something from the second list.

What each format is for

The two are aimed at different work: NDJSON at moving data between programs and streaming, TOML at editing. That is worth weighing before converting, because the reason one exists is usually the reason the other is awkward.

TOML dates from 2013, specified as TOML 1.0. Visual Studio Code reads it.

NDJSON to TOML: table names, nulls and how much is too much

Are my NDJSON files uploaded anywhere?

No. This conversion runs entirely inside your browser, so the file never leaves your device. You can confirm it yourself: open the network tab of your browser's developer tools and convert something. You will see the page load, plus the analytics and advertising the site is paid for with — and nothing carrying your file.

What does the TOML output look like?

One [[items]] block per line of the source, with that record’s fields underneath it. TOML calls this an array of tables, and it is the natural home for a list of records.

Can I change the items key?

Not during the conversion — a TOML document has to be a table, so the list needs a name and items is the placeholder. Renaming it is a search and replace on the block headers, and it is the first edit to make.

Do the records have to have the same fields?

No. Each block carries the keys its line had, and TOML is perfectly happy with that. It is one of the few targets in this group where a heterogeneous file needs no reconciliation at all.

What happens to null values?

They disappear. TOML has no null, so a key whose value was null is not written. If the absence of a key means something different to your tool than an empty value, check the file for nulls before converting.

Is this a good idea for a large file?

No. TOML is a configuration format meant to be read by people, and a thousand [[items]] blocks is not that. Above a few dozen records, CSV, JSON or a database is the right destination.

Is the file uploaded?

No. Both halves are JavaScript running in this page, so nothing is sent anywhere.

More about these formats