The task that taught me to respect flattening looked boring on the ticket: "export the settings object to a spreadsheet so the client can edit it." The settings were a deeply nested JSON blob out of a Laravel config, three and four levels deep in places, with arrays of feature flags mixed in. A spreadsheet has columns, not trees. I needed every leaf value as a single flat key the client could see in one row, edit, and hand back, and then I needed to rebuild the exact nested structure from their edits without losing a single array index. That round trip, nested to flat and flat back to nested, is what a JSON flattener does, and getting it lossless is the whole game. The Toolz JSON flattener is the tool I built for that job, and this guide explains how it works and where it saves you.
TL;DR: A JSON flattener converts a nested object into a single-level object whose keys are the path to each value, so
{"user":{"city":"Austin"}}becomes{"user.city":"Austin"}. Arrays flatten with their index in the path (roles.0). Unflatten reverses it, rebuilding the nested objects and arrays from those dotted keys. The Toolz tool does both directions losslessly, preserves empty objects and arrays, supports dot or bracket array notation and a custom delimiter, and runs entirely client-side with no upload and no signup.
I build SaaS on Laravel and React and ship WordPress plugins, so nested JSON is everywhere in my day: config files, API payloads, i18n message catalogs, feature-flag stores. The systems that consume those payloads, though, frequently want flat keys. Flattening is the adapter between a shape that is natural for structured data and a shape that is natural for spreadsheets, environment variables, and dotted key stores. It is a small operation that quietly unblocks a lot of otherwise annoying tasks.
What is a JSON flattener?
A JSON flattener converts a nested JSON object into a single-level object where each key is the full path from the root to a leaf value. The path syntax is not arbitrary either: RFC 6901 defines JSON Pointer, the standard way to address one value inside a document. The nested {"user":{"name":"Ada","address":{"city":"Austin"}}} becomes the flat {"user.name":"Ada","user.address.city":"Austin"}. Nothing about the values changes; only their addressing does. Every leaf, however deep, gets one key that spells out how to reach it.
Arrays are handled the same way, with the array index becoming a path segment: {"roles":["admin","editor"]} flattens to {"roles.0":"admin","roles.1":"editor"}. The index in the key is what lets the reverse operation rebuild the array in the correct order. That reverse operation, unflatten, takes a flat object of dotted keys and reconstructs the nested objects and arrays, deciding at each step whether a path segment names an object property or an array index. When flatten and unflatten use the same delimiter and array notation, they are exact inverses, and that lossless round trip is the property that makes the tool trustworthy for real work rather than a one-way convenience.
Why would I flatten JSON in the first place?
Because a lot of systems only speak flat key-value pairs, and a dotted path is the standard way to carry nested structure through them without losing it. The clearest example is a spreadsheet. A spreadsheet is a grid of columns, and there is no column for "the city inside the address inside the user." Flatten the object and user.address.city becomes a single column header the value can live under, which is exactly what my settings-export ticket needed.
The same shape shows up in many other places. Environment variables and .env files are flat, and tools that map config to them use dotted or underscored paths. Internationalization libraries key their strings with dotted paths like checkout.button.label. Feature-flag platforms, analytics event schemas, and a lot of logging pipelines all want one level of keys, often so they can index or query each field independently. In every case the nesting is real and meaningful, and flattening preserves it in the path rather than discarding it. When the flat representation has served its purpose, unflatten rebuilds the original object. If you are also moving between JSON and spreadsheets specifically, the CSV to JSON converter is the natural companion for the tabular leg of that trip.
How are arrays handled when flattening?
Arrays are the part people most often get wrong, so it is worth being precise. An array element is flattened using its numeric index as a path segment, which preserves order and lets the array be rebuilt exactly. The Toolz tool offers two notations for that index, and they encode the same information with different syntax.
| Input | Dot notation | Bracket notation |
|---|---|---|
{"roles":["admin","editor"]} |
roles.0, roles.1 |
roles[0], roles[1] |
{"items":[{"id":1}]} |
items.0.id |
items[0].id |
{"matrix":[[1,2]]} |
matrix.0.0 |
matrix[0][0] |
Dot notation writes roles.0, which is compact and matches how many config and i18n systems key their values. Bracket notation writes roles[0], which mirrors JavaScript access syntax and the path style some query languages use. Neither is more correct; you pick the one the system on the other end expects. The important thing is consistency: whatever notation you flatten with, use the same one to unflatten, because that is how the tool knows a numeric segment means "array index" rather than "an object key that happens to be a number." The Toolz flattener defaults to dot notation and lets you switch to bracket with one click.
How do I use the JSON flattener?
The flow has two directions and a couple of options. Start by choosing flatten or unflatten with the toggle at the top. Flatten takes nested JSON and produces dotted keys; unflatten takes dotted keys and rebuilds the nested structure. Load Sample fills in a worked example for whichever direction you are in, so you can see the shape of both the input and the output before pasting your own data.
Paste your JSON into the input box. For flatten, that is a nested object; for unflatten, it is a flat object whose keys are dotted paths. The input has to be valid JSON, and if it is not, the tool reports the parser's message and the position of the problem instead of handing you a blank output, so a stray trailing comma is quick to find. Set the key style: the delimiter defaults to a dot and can be changed when your keys already contain dots, and the array notation toggle switches between roles.0 and roles[0]. Keep those two settings identical across a flatten and its matching unflatten so the round trip lines up. Click the action button, check the key count that confirms how many leaves were produced, and copy the output or download it as a .json file. The whole thing runs the instant you click, in your browser, with nothing sent anywhere.
Is flattening lossless, and what are the edge cases?
For the Toolz tool, yes, with one edge case worth knowing. Scalars, null, booleans, and numbers survive untouched. The subtle part is empty containers: an empty object {} or an empty array [] is preserved as a leaf value rather than silently dropped, so a config that relies on an empty list still round-trips to exactly what it was. A lot of naive flatteners lose empty containers, which quietly changes the meaning of a document, so this is a deliberate choice.
The one genuine limitation is keys that already contain the delimiter. If your data has an object key with a literal dot in it, like {"a.b":1}, and you flatten with a dot delimiter, the path becomes ambiguous: unflatten cannot tell whether a.b was one key or a nested a then b. The fix is to change the delimiter to a character that does not appear in your keys, such as a slash or a pipe, and use that same delimiter for both directions. It is the same class of escaping problem you see when CSV delimiters collide with the data, and the same solution applies: pick a separator your values do not contain. Outside that case, a flatten followed by an unflatten with matching settings reproduces the original document exactly, arrays and empty containers included.
What is the difference between flattening and converting to another format?
Flattening is not a format conversion; it is a restructuring within JSON. The output of a flatten is still JSON, just with a single level of dotted keys instead of nesting. That is different from the converters that turn JSON into a genuinely different serialization, and knowing which one you need saves a step.
If you want the same data expressed as YAML for a config file, the JSON to YAML converter is the tool, because it changes the syntax rather than the shape. If you want a TypeScript interface describing the structure so your front end knows what it is handling, the JSON to TypeScript converter infers that from a sample. If you want a formal contract for validation, the JSON schema generator derives a JSON Schema from an example. Flattening sits alongside these rather than replacing them: you flatten when a downstream system wants one level of keys, and you convert when a system wants a different language. I frequently do both in sequence, flattening a payload to edit it as columns and then, separately, generating types for the nested version I hand to React. For pretty-printing or validating the JSON at any point in that chain, the JSON formatter is the utility I keep open in the next tab.
How does unflatten decide between an array and an object?
This is the mechanism that makes the round trip work, and understanding it explains both the tool's defaults and its one sharp edge. When unflatten reads a key like user.roles.0, it splits it into the segments user, roles, and 0, then walks them building structure as it goes. At each step it looks at the next segment to decide what kind of container to create. If the next segment is a non-negative integer, it makes an array; if it is anything else, it makes an object. So roles.0 tells the tool that roles should be an array whose first element is the value, while address.city tells it that address should be an object with a city property.
That single rule, "integer segment means array index," is why a flatten and an unflatten with matching settings reproduce arrays exactly. It is also the reason the array notation you choose has to be consistent. In dot notation the array index and an object key are both just text between dots, so the tool relies entirely on the integer test. In bracket notation the [0] syntax makes the intent explicit before the split even happens. Either works, but mixing them, flattening with brackets and unflattening with dots, breaks the signal the tool depends on.
Here is the sharp edge that follows from the rule: an object whose keys genuinely are the strings "0", "1", "2" is indistinguishable, once flattened, from an array. Both produce the keys 0, 1, 2, and unflatten will rebuild them as an array because that is the overwhelmingly common case. If you truly need an object with numeric string keys to survive the round trip, that is the one situation where flattening and unflattening will not give you back the exact type, and you are better off keeping that object nested or using a non-numeric key scheme. In years of doing this I have hit that case exactly twice, both times in data that was arguably modeled wrong to begin with, so the default is the right trade for almost everyone. It is the same kind of pragmatic decision behind keeping numbers with leading zeros as strings in the CSV to JSON converter: the tool optimizes for what the data almost always means, and documents the rare exception rather than making the common path awkward.
Is my JSON safe to paste into this tool?
Yes, and it is architectural rather than a promise. Parsing, flattening, unflattening, and serializing all run as JavaScript inside your own browser tab. There is no upload, no server round trip, and nothing is logged or stored. Open your browser's network tab and click the action button: no request goes out. Once the page has loaded you can disconnect from the internet and it keeps working, because there was never a server in the loop to begin with.
This matters more than it might seem, because JSON payloads are exactly the kind of thing that carries secrets: API tokens in a config, personal data in an API response, internal identifiers in a settings blob. A flattener that uploaded your object to a server would turn a private payload into someone else's log line. Client-side processing removes the risk entirely, which is why it is the default across every tool on toolz.dev. If you want the full reasoning, I laid it out in the data privacy guide for online tools, and the broader kit I use for this kind of work is in my web developer toolkit guide.
Frequently asked questions
What does it mean to flatten JSON?
Flattening JSON converts a nested object into a single-level object whose keys are the path to each value. The nested {"user":{"city":"Austin"}} becomes the flat {"user.city":"Austin"}. Every leaf value gets one dotted key, and no nesting remains in the output. Unflatten reverses the process and rebuilds the nested structure.
How are arrays handled when flattening?
Array elements are flattened using their index as a path segment. {"roles":["admin","editor"]} becomes {"roles.0":"admin","roles.1":"editor"} in dot notation, or {"roles[0]":"admin","roles[1]":"editor"} in bracket notation. The index preserves order so the array can be rebuilt exactly when you unflatten.
Can I unflatten dotted keys back into nested JSON? Yes. Switch to unflatten mode and paste a flat object whose keys are dotted paths. The tool rebuilds the nested objects and arrays, deciding array versus object by whether each path segment is an integer index. Flatten and unflatten are exact inverses when the delimiter and array notation match.
What is the difference between dot and bracket array notation?
Both encode the same array index; only the syntax differs. Dot notation writes items.0.id, which is compact and common in config and i18n keys. Bracket notation writes items[0].id, which matches JavaScript access syntax and some query languages. Pick whichever your target system expects, and use the same one for both directions.
Does flattening lose any data? No. Scalars, nulls, booleans, and even empty objects and empty arrays are preserved as leaf values, so unflattening the result reproduces the original document. The one thing to watch is keys that already contain your delimiter, which is why the separator is configurable.
Why would I flatten JSON? Flattening adapts nested data to systems that expect one level of keys: spreadsheet columns, environment variables, dotted translation keys, feature-flag stores, and many analytics and logging pipelines. The dotted path carries the original structure through a flat format so nothing is lost.
What happens if a key already contains a dot? A literal dot inside a key is ambiguous with the path separator, so the round trip can split it in the wrong place. Change the delimiter to a character that does not appear in your keys, such as a slash or a pipe, and use that same delimiter for both flatten and unflatten.
Is my JSON uploaded anywhere? No. Parsing and transformation run as JavaScript in your browser. Nothing is transmitted, logged, or stored. You can confirm this by watching the network tab while flattening, or by going offline, since the tool keeps working without a connection.



