T tomlkit·org
Inspect Formatter Validator Lint Stats Keys Query Convert TOMLJSON JSONTOML TOMLYAML YAMLTOML INITOML TOMLINI .envTOML TOML.env TOMLTS TOMLCSV CSVTOML TOMLXML TOML.properties .propertiesTOML Transform Sort keys Flatten Unflatten Redact Minify Generate Go struct Rust struct Python types JSON Schema Compare Diff Merge

TOML Unflatten

updated 20 August 2026

Feed it a flat file of dotted keys — server.tls.cert = "…", hosts.0 = "a" — and it rebuilds the nested tables and arrays those keys describe. It is the return trip for anything that flattened your config: an env-var dump, a properties export, a key/value store.

numeric segments become arrays

What this tool does

It reads a document whose keys carry their own hierarchy as text and turns that text back into structure. Every separator in a key becomes one level of nesting, so server.tls.cert ends up as a cert key inside a [server.tls] table. Runs of all-digit segments become arrays when they are dense and zero-based, which is how hosts.0 / hosts.1 turns back into hosts = ["alpha", "beta"].

The intent it closes: "something handed me one line per setting and I want a real config back." That something is usually a .properties export, a Consul or etcd dump, a spreadsheet column, or the output of a flattener earlier in a pipeline.

It is the exact inverse of the TOML Flattener with matching options — flatten then unflatten and you get your structure back, modulo comments, which no parse-and-re-emit tool preserves.

When you'd reach for it

  • Rebuild a config from a key/value store. Consul, etcd, and SSM parameter dumps are flat by nature; this puts the tables back.
  • Finish a .properties migration. Convert with .properties to TOML first if you have the raw file, or paste the flat TOML here if something already quoted the keys.
  • Undo a flatten. You flattened a config to grep it, edited a value, and now want the nested form back.
  • Turn a spreadsheet column into a config. Two columns of key and value become a real document once the dots are expanded.
  • Recover arrays from indexed keys. ports.0, ports.1, ports.2 collapse into a single TOML array.

How it works

Three steps, and the middle one is where all the decisions live.

1. Parse the flat document

The input is parsed as ordinary TOML first, so both spellings work: quoted keys that contain literal dots ("server.port" = 8080) and bare dotted keys that the parser itself nests (server.port = 8080). Either way the result is normalised to one flat map of full paths before rebuilding starts.

2. Split and rebuild

Each path is split on the separator and walked, creating tables as needed. Bracket notation is understood too: hosts[0] is treated the same as hosts.0. If two keys disagree about a level — one wants a to be a value, another wants it to be a table — the table wins, because a scalar cannot hold children.

3. Promote numeric tables to arrays

With Numeric keys set to Rebuild as arrays, any table whose keys are exactly 0…n-1 becomes an array in index order. Sparse or non-zero-based sets (1, 2, 7) are left as tables, because turning them into an array would silently invent or drop elements.

Options reference

Separator

The character or string that divides levels in your keys. . is the default and matches TOML, __ is the usual convention for environment variables, and / shows up in key/value stores. Whatever you set is replaced with a dot internally before rebuilding, so mixed input still works as long as one separator dominates.

Numeric keys

Rebuild as arrays converts dense zero-based numeric tables into TOML arrays — the right choice for output that came from a flattener. Keep as tables leaves them as [hosts.0]-style tables, which you want when the numbers are genuinely identifiers (a map keyed by user ID, say) rather than positions.

Sort

Keep order emits keys in the order they first appeared in the flat input. Alphabetical sorts every table, which makes the result diff-stable regardless of how the flat file was ordered.

Example

Input:

"server.host" = "0.0.0.0"
"server.tls.enabled" = true
"hosts.0" = "alpha"
"hosts.1" = "beta"

Output:

hosts = [ "alpha", "beta" ]

[server]
host = "0.0.0.0"

[server.tls]
enabled = true

The two hosts.N keys were dense and zero-based, so they became an array. server and server.tls became real section headers. Set Numeric keys to Keep as tables and you would get [hosts] with keys 0 and 1 instead.

Limits and notes

  • Comments are not preserved. The parser does not carry them into the tree, so they cannot be re-emitted.
  • Real dots in a key are indistinguishable from separators. A key that genuinely contains a dot — a version number, a hostname — will be split. Use a different separator for those files, or quote and unflatten in two passes.
  • Sparse indexes stay tables. By design: promoting 0, 1, 5 to an array would have to invent the gaps.
  • Type inference is not applied. The input is TOML, so values already have types. If your source is untyped text, run it through .properties to TOML, which does infer numbers and booleans.

FAQ

How is this different from the Flattener with reverse turned on?

Same engine, different framing. The Flattener has a reverse switch buried among its flatten options; this page is the reverse direction with its own controls — separator, numeric-key policy, sort — and no flatten-only settings in the way.

Do my keys have to be quoted?

No. Both "server.port" = 8080 and server.port = 8080 work. The first is a literal key containing a dot; the second is a dotted key the TOML parser already nests. Both end up in the same flat map before rebuilding.

What if two keys conflict?

If one key needs a level to be a table and another needs it to be a scalar, the table wins and the scalar is overwritten — a value cannot hold children. Check the input if you did not expect a nesting level to appear.

Does it handle arrays of tables?

Yes, when the flat keys spell them out with indexes: servers.0.name and servers.1.name rebuild into two [[servers]] blocks. That requires the indexes to be dense and start at zero.

Is the file uploaded?

No. Everything runs in the browser tab, so flat dumps that contain credentials never leave your machine.