Flatten JSON (and Unflatten)

Examples:

Last updated: 2026-09-17

Paste JSON to flatten it into dot-notation key paths (e.g., users.0.name), or switch to unflatten mode to rebuild nested JSON from flat paths. Supports custom delimiters and bracket notation. Everything runs locally.

What this tool does

Flattens nested JSON into a single-level object with dot-notation keys, or unflattens dot-notation keys back into nested JSON. You can choose the delimiter (default: .) and whether array indices use bracket notation (items[0]) or dot notation (items.0). The two modes are inverses: flatten encodes a hierarchy into flat paths, and unflatten decodes those paths back into the nested form, provided the delimiter and index style match across both steps.

Example

Input (nested):

{"user":{"name":"Alice","address":{"city":"NYC","zip":"10001"}},"tags":["admin","user"]}

Output (flattened, bracket notation):

{
  "user.name": "Alice",
  "user.address.city": "NYC",
  "user.address.zip": "10001",
  "tags[0]": "admin",
  "tags[1]": "user"
}

Reverse the process: paste the flat object and switch to Unflatten mode to rebuild the nested structure.

Flatten vs unflatten

ModeInputOutput
Flatten{"a":{"b":1}}{"a.b":1}
Unflatten{"a.b":1}{"a":{"b":1}}
Flatten (array){"items":[{"x":1}]}{"items[0].x":1}
Unflatten (array){"items[0].x":1}{"items":[{"x":1}]}

Options

OptionDefaultEffect
Delimiter.Separator between path segments
Array index styleBracketitems[0] (bracket) or items.0 (dot)

When to use flattening

Flattening for CSV export

Most CSV libraries expect a flat list of column names and one value per cell. Nested objects break that model because a single field like address maps to several sub-fields. Flatten first, then write the flat object as one CSV row.

Given a list of orders:

[
  {"id":"ord_1","customer":{"name":"Alice","email":"alice@example.com"},"total":42.50,"items":2},
  {"id":"ord_2","customer":{"name":"Bob","email":"bob@example.com"},"total":13.00,"items":1}
]

Flatten each order (delimiter .) and collect every distinct key as a column header:

id,customer.name,customer.email,total,items
ord_1,Alice,alice@example.com,42.50,2
ord_2,Bob,bob@example.com,13.00,1

The column names are the flattened keys, so downstream consumers like pandas, R, or a spreadsheet import the file without a second parsing step. If your objects carry inconsistent keys across rows, union the key sets first so every row aligns under the same header order. Arrays of scalars (such as tags) are best handled separately, either by joining them into a single cell or by exploding them into one row per element before flattening.

Flattening for key-value databases

DynamoDB, Redis, and similar stores map a single string key to a single scalar value. They do not traverse nested objects. Flattening produces one row per leaf, which maps directly onto a PutItem or HSET call.

Take this product document:

{"sku":"abc-123","price":{"amount":19.99,"currency":"USD"},"stock":{"warehouse":"us-east","qty":120}}

Flattened with a colon delimiter (useful when keys themselves contain dots):

{
  "sku": "abc-123",
  "price:amount": 19.99,
  "price:currency": "USD",
  "stock:warehouse": "us-east",
  "stock:qty": 120
}

In Redis you can pipe this straight into HSET product:abc-123 with each flat key as a hash field. In DynamoDB the flat keys become attribute names on the item. For range queries over a hierarchy, a slash delimiter (stock/qty) pairs well with prefix scans in key-ordered stores such as LevelDB or RocksDB, because every child path sorts immediately after its parent prefix.

Round-tripping: flatten then unflatten

A round trip takes nested JSON, flattens it, then unflattens the flat result. The final object equals the original when you keep the delimiter and array index style identical in both directions. The tool’s unflatten step parses bracket indices and numeric dot segments back into real array positions, so arrays reconstruct at the correct indices rather than collapsing into objects.

{"server":{"region":"us-east","ports":[8080,8443]},"enabled":true}

Flatten (bracket notation):

{
  "server.region": "us-east",
  "server.ports[0]": 8080,
  "server.ports[1]": 8443,
  "enabled": true
}

Unflatten that output and you get the original document back, with server.ports restored as an array of two numbers. Round trips are handy when you edit the flat form by hand, sort keys, or merge two configs at the leaf level, and then want the nested shape again. Keep the delimiter consistent on both sides: if you flatten with /, unflatten with / too, otherwise the parser treats the dots in the keys as literal characters and nests under the wrong segments.

Common mistakes when flattening

Choosing a delimiter

The delimiter joins path segments, so pick one that never appears inside your keys. Dot (.) is the default and reads naturally for most JSON, but it clashes with keys that already use dots, such as version strings (v2.1.0) or domain names. Slash (/) suits hierarchical or filesystem-like data and aligns with prefix scans in key-ordered stores. Colon (:) is compact and rare in object keys, which makes it a safe default for key-value database loads. Pipe (|) is bulky but unambiguous when both dots and slashes appear in your data. Whatever you choose, apply the same delimiter in both directions so the round trip stays lossless. If you target a system that already speaks JSONPath, keep dot for object segments and bracket for array indices, because that mirrors the query syntax the downstream reader expects.

Flattening nested arrays

Arrays add a second dimension to the path. The tool offers two styles for the index segment. Bracket notation wraps the index in square brackets (items[0], items[1].name), which keeps the index visually separate from object keys and is the default. Dot notation writes the index as a normal path segment (items.0, items.1.name), which is shorter and convenient when downstream code splits on a single delimiter without special bracket handling.

For a document with an array of objects:

{"orders":[{"id":"ord_1","total":42.5},{"id":"ord_2","total":13.0}]}

Bracket output:

{
  "orders[0].id": "ord_1",
  "orders[0].total": 42.5,
  "orders[1].id": "ord_2",
  "orders[1].total": 13.0
}

Dot output (same data, dot style):

{
  "orders.0.id": "ord_1",
  "orders.0.total": 42.5,
  "orders.1.id": "ord_2",
  "orders.1.total": 13.0
}

Bracket notation is safer when object keys can be numeric, because the parser distinguishes a bracketed index from a plain key during unflatten. Dot notation relies on a numeric test for each segment, so a key like 0 is treated as an array index even when the original structure used an object. If you control the schema and no object key is ever a bare integer, dot notation is fine. When arrays nest inside arrays, bracket style stacks cleanly (matrix[0][1]), while dot style flattens both indices into the same delimiter stream (matrix.0.1), which still unflattens correctly because each numeric segment is recognized in turn.

Do it in code

JavaScript:

function flatten(obj, prefix = '', result = {}) {
  for (const [key, value] of Object.entries(obj)) {
    const path = prefix ? `${prefix}.${key}` : key;
    if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
      flatten(value, path, result);
    } else if (Array.isArray(value)) {
      value.forEach((item, i) => flatten(item, `${path}[${i}]`, result));
    } else {
      result[path] = value;
    }
  }
  return result;
}

Python (flatten-dict):

from flatten_dict import flatten
flat = flatten(data, reducer='dot')
# Unflatten:
from flatten_dict import unflatten
nested = unflatten(flat, splitter='dot')

CLI (jq):

# Flatten with pathto
echo '{"a":{"b":1}}' | jq 'path(.. | scalars) as $p | {($p | join(".")): getpath($p)}' | jq -s 'add'

Reading flat paths in your code

Once data is flat, you can address any leaf with a string. This removes the need for optional chaining or nested guards when you pull values out. In JavaScript, split the path on the delimiter and reduce:

const get = (obj, path, delimiter = '.') =>
  path.split(delimiter).reduce((acc, segment) => acc[segment], obj);

Because the tool writes bracket indices as items[0], a pure split on . leaves the bracket attached. Strip brackets first, or parse the path the same way the unflatten step does: scan for [ and ], push the enclosed number as its own segment, and split the remainder on the delimiter. Keeping a single helper for both reading and rebuilding avoids drift between the two operations.

Performance and limits

Flattening runs in a single recursive pass over the document, so its cost grows linearly with the number of leaves. Every leaf becomes one entry in the output object, and every internal node becomes part of a key string, which means deeply nested documents produce long keys and a large flat map. For payloads in the megabyte range this is negligible. For very wide or very deep inputs, watch the total key length, since some downstream systems cap attribute names or hash field lengths.

The operation runs entirely in your browser. Nothing is uploaded, so you can flatten credentials, internal configs, or customer records without them leaving the tab. There is no network call and no server log, which makes the tool suitable for sensitive data that you would not paste into a hosted API.

Migrating a schema with flattening

When a nested field moves or renames between two API versions, diffing the flat forms is often the quickest way to see every affected path. Flatten both versions, sort the keys, and compare line by line. Renames show up as a removed key and an added key; relocated branches show up as the same suffix under a different prefix. Because the flat form has one row per leaf, a standard text diff pinpoints the exact paths that changed without you tracing the nesting by eye.

This is cheaper than writing a custom recursive comparator for a one-off migration check, and the flat output doubles as a checklist for updating queries, indexes, or templates that reference the old paths.

Validating the output

After a round trip, compare the unflattened document against the original to confirm parity. Serialize both to a canonical string with sorted keys and compare the strings, or compute a checksum over the sorted flat key list. A mismatch usually means the delimiter was not held constant across directions, or that a key contained the delimiter character and silently split into extra segments. The tool preserves null, booleans, numbers, and empty containers as leaf values, so those should match exactly; if a null disappears or an empty array turns into an absent key, the input was edited between steps rather than transformed by the tool itself.

Edge cases handled

Frequently asked questions

What is JSON flattening?
Flattening converts nested JSON into a single-level object where keys are dot-separated paths. For example, an object with key a containing key b equal to 1 becomes the single key a.b equal to 1. This is useful for CSV export, database storage, and data analysis where you need one row per leaf value.
What is unflattening?
Unflattening is the reverse. It takes a flat object with dot-notation keys and rebuilds the nested structure. The single key a.b equal to 1 becomes an object with key a containing key b equal to 1. The tool detects array indices in bracket notation and rebuilds arrays accordingly.
How are arrays handled in flattening?
Arrays are converted to bracket notation by default: items at index 0 become items[0], index 1 become items[1], and so on. You can switch to dot notation (items.0, items.1) if preferred. Bracket notation is more common because it visually distinguishes array indices from object keys.
Can I use a custom delimiter?
Yes. The default delimiter is a dot, but you can change it to any character like slash, colon, or pipe. This is useful when your JSON keys already contain dots, which would create ambiguous paths with the default delimiter.
Why would I flatten JSON?
Flattening is useful for converting JSON to CSV (each flat key becomes a column), storing in key-value databases like DynamoDB or Redis, comparing structures with diff tools, and feeding data into tabular tools like spreadsheets or SQL databases.
How do I flatten JSON in Python?
Use the flatten-dict package: import flatten from flatten_dict and call flatten(data, reducer='dot'). Or use pandas.json_normalize for tabular flattening of arrays of objects. Install with pip install flatten-dict pandas.
Does unflatten reject invalid input?
Yes. Unflatten mode only accepts a flat JSON object. If you pass an array, a number, a string, or null, the tool returns the error that the input must be a flat object with dot-notation keys. Feed it the output from flatten mode for a clean round trip.
What happens to empty objects and arrays?
Empty objects and empty arrays are preserved as leaf values rather than expanded. An empty object at key meta stays as meta equal to an empty object, and an empty array at key tags stays as tags equal to an empty array. They are not dropped from the output.
Is the delimiter restricted to a single character?
No. You can type any string as the delimiter, including a hyphen, an arrow, or a short token. The only rule is that the delimiter must not appear inside your object keys, otherwise unflatten cannot tell where one path segment ends and the next begins.
Does flattening preserve key insertion order?
Yes. The walk visits object keys in the order they appear in the parsed input, and the flat result keeps that order. This mirrors the standard JavaScript behavior for string-keyed objects, so the flat output reads top to bottom just like the original document.

Related tools