{}JSONLint.app

Flatten nested JSON to a single level, or unflatten back to nested structure:

Mode:
Loading...
Loading...

What is JSON Flattening?

Flattening converts a nested JSON structure into a single-level object where nested keys are joined with a delimiter. This is useful for data processing, database storage, and CSV export.

Example

Nested JSON:

{
  "user": {
    "name": "John",
    "address": {
      "city": "Boston"
    }
  }
}

Flattened (with dot delimiter):

{
  "user.name": "John",
  "user.address.city": "Boston"
}

Array Handling

Arrays are flattened with bracket notation:

// Input
{
  "tags": ["javascript", "typescript"],
  "users": [
    { "name": "Alice" },
    { "name": "Bob" }
  ]
}

// Output
{
  "tags[0]": "javascript",
  "tags[1]": "typescript",
  "users[0].name": "Alice",
  "users[1].name": "Bob"
}

Delimiter Options

DelimiterExample KeyUse Case
. (dot)user.address.cityMost common, JavaScript-like
/ (slash)user/address/cityPath-like, Firebase RTDB
_ (underscore)user_address_cityEnvironment variables
__ (double)user__address__cityAvoids conflicts with underscores

Common Use Cases

  • CSV export — Flatten before converting to CSV for spreadsheet-friendly data
  • Search indexing — Elasticsearch and other search engines work better with flat documents
  • Key-value stores — Redis, DynamoDB, and similar databases
  • Environment variables — Config management systems
  • Form data — HTML form serialization

Unflatten

The reverse operation reconstructs nested objects from flattened keys. Use this when you need to restore the original structure after processing.

Programmatic Flattening

JavaScript

function flatten(obj, prefix = '', delimiter = '.') {
  return Object.entries(obj).reduce((acc, [key, value]) => {
    const newKey = prefix ? `${prefix}${delimiter}${key}` : key;
    
    if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
      Object.assign(acc, flatten(value, newKey, delimiter));
    } else {
      acc[newKey] = value;
    }
    
    return acc;
  }, {});
}

Python

def flatten(obj, prefix='', delimiter='.'):
    result = {}
    for key, value in obj.items():
        new_key = f"{prefix}{delimiter}{key}" if prefix else key
        if isinstance(value, dict):
            result.update(flatten(value, new_key, delimiter))
        else:
            result[new_key] = value
    return result

Command Line (with jq)

# Flatten with jq (recursive)
jq '[paths(scalars) as $p | {"key": $p | join("."), "value": getpath($p)}] | from_entries' data.json

Pro Tips

  • 💡 Choose delimiter wisely — If your keys contain dots, use a different delimiter to avoid confusion
  • 💡 Preserve array indices — The bracket notation preserves order when unflattening
  • 💡 Use with CSV — Flatten first, then useJSON to CSV for spreadsheet export

Related Tools

Common Mistakes & Pro Tips

  • Flattening builds path keys from nestingA nested object like {"user":{"name":"Sam"}} becomes {"user.name":"Sam"}, where the new key is the dot-joined path to each leaf value. This is convenient for diffing, spreadsheets, and environment-variable style configs, but the result is no longer the original shape.
  • Array indices use bracket or dotted-index notationArrays flatten with their numeric index, commonly as items[0].id or items.0.id depending on the convention chosen. To round-trip cleanly back to an array on unflatten, the tool must recognize purely numeric path segments and rebuild them as array positions rather than object keys.
  • Keys containing dots collide with the path separatorIf an original key literally contains a dot, such as {"a.b":1}, it becomes indistinguishable from a nested path a -> b after dot-flattening, so unflatten cannot tell them apart. Use bracket notation or a separator that does not appear in your keys to avoid silent data corruption.
  • Empty objects and arrays have no leaf to flattenBecause flattening only emits keys for leaf values, an empty {} or [] disappears in a naive implementation, so unflatten cannot restore it. A correct round-tripper either preserves empties as explicit entries or you should be aware the empty containers may be lost.
  • Unflatten reconstructs structure but can't recover lost order or typesGoing from flat keys back to nested JSON rebuilds objects and arrays from the path segments, inferring an array when a segment is a non-negative integer. Sparse or non-sequential indices may produce arrays with gaps (null/undefined holes), so verify the rebuilt shape matches your intent.

Frequently Asked Questions

What does flattening JSON actually produce?

It produces a single-level object whose keys are the full paths to each leaf value in the original. For example {"a":{"b":1}} becomes {"a.b":1}. This makes nested data easy to scan, diff line by line, or load into a flat key/value store.

How do nested arrays get flattened?

Each element gets a key that includes its index, like tags[0] and tags[1], or tags.0 and tags.1 depending on the notation you pick. On unflatten, segments that are non-negative integers are interpreted as array positions so the array is rebuilt rather than turned into an object.

What happens if my keys already contain dots?

Dotted keys are ambiguous with the dot path separator, so {"a.b":1} and {"a":{"b":1}} flatten to the same thing and cannot be reliably reversed. To stay safe, use bracket notation or a custom separator that never appears in your real keys.

Can I always unflatten back to the exact original?

Usually, but not in every case. Round-tripping is lossless for typical data, but it can fail when keys contain the separator, when objects are empty, or when array indices are sparse. Test a round-trip on a sample before relying on it for important data.

How do I flatten JSON for use in a spreadsheet or .env file?

Flatten with dot or underscore separators so each leaf becomes one flat key, then map those keys to columns or environment-variable names. Underscores are often preferred for .env files because variable names cannot contain dots in most shells.

Is flattening reversible and does it change values?

Flattening never changes the leaf values themselves, only how the keys are expressed. It is reversible via unflatten in most cases, with the caveats around dotted keys, empty containers, and sparse arrays noted above. The values you put in are the values you get back.