# Flatten JSON

Flatten nested JSON into single-level dot or bracket key paths, or unflatten those paths back into nested JSON. Custom separator, CSV/pairs output, no upload.

## Run it

- **CLI:** `gizza tool flatten-json '{"user":{"name":"Ada","tags":["admin","beta"]},"active":true}'`
- **Web:** https://gizza.ai/tools/flatten-json/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/flatten-json/tool.json

## Inputs

- `json` — JSON document _(field)_
- `direction` — Direction _(field)_
- `separator` — Key separator _(field)_
- `array_notation` — Array index notation _(field)_
- `max_depth` — Max depth (0 = unlimited) _(field)_
- `flatten_arrays` — Expand arrays into indexed paths _(field)_
- `preserve_empty` — Keep empty objects and arrays _(field)_
- `key_case` — Key case _(field)_
- `output` — Output format _(field)_
- `pretty` — Pretty-print JSON output _(field)_
- `indent` — Indent (spaces) _(field)_

## Output

- Result (text)

## Query parameters

Open the tool pre-filled and auto-run via URL:

- `json` — JSON document
- `direction` — Direction
- `separator` — Key separator
- `array_notation` — Array index notation
- `max_depth` — Max depth (0 = unlimited)
- `flatten_arrays` — Expand arrays into indexed paths
- `preserve_empty` — Keep empty objects and arrays
- `key_case` — Key case
- `output` — Output format
- `pretty` — Pretty-print JSON output
- `indent` — Indent (spaces)

Example: `https://gizza.ai/tools/flatten-json/?json=%7B%22user%22%3A%7B%22name%22%3A%22Ada%22%2C%22tags%22%3A%5B%22admin%22%2C%22beta%22%5D%7D%2C%22active%22%3Atrue%7D&direction=flatten&separator=.&array_notation=bracket&max_depth=0&flatten_arrays=true&preserve_empty=true&key_case=preserve&output=json&pretty=true&indent=2`

---

## About this tool

**Flatten JSON** collapses a nested JSON document into a single level of
**path → value** pairs, so `{"user":{"name":"Ada"}}` becomes `{"user.name":"Ada"}`.
It also runs the other way: point it at a flat map of paths and it rebuilds the
nested document. Both directions are lossless, so you can flatten, edit or diff
the flat form, and unflatten it back. Everything runs locally in your browser —
your JSON is never uploaded.

Paths use the familiar **lodash / `dot-object` / `flat`** style, not RFC 9535
JSONPath:

- **Object keys** are joined by the **separator** — `.` by default (`user.address.city`),
  but `_` or `/` or any 1–8 characters work.
- **Array elements** use the **array index notation** — `bracket` (default) writes
  `tags[0]`, `separator` writes `tags.0`.

### Worked example

Given this document:

```json
{"user":{"name":"Ada","tags":["admin","beta"]},"active":true}
```

flattening with the defaults gives:

```json
{
  "user.name": "Ada",
  "user.tags[0]": "admin",
  "user.tags[1]": "beta",
  "active": true
}
```

Paste that result back in with **direction = unflatten** and you get the original
nested document, byte for byte — key order is preserved, not alphabetised.

Change the output format to get the same data in a different shape. With
`separator = _`, `key case = upper` and `output = pairs`, the document
`{"db":{"host":"localhost","port":5432},"debug":false}` becomes an env-file:

```text
DB_HOST=localhost
DB_PORT=5432
DEBUG=false
```

and `output = csv` gives a two-column sheet you can paste straight into a
spreadsheet:

```text
key,value
order.id,A-17
order.items[0].sku,X1
order.items[0].qty,2
```

`output = paths` prints just the path list — handy for auditing which fields an
API actually returns.

### Options worth knowing

- **Direction** — `flatten` (default), `unflatten`, or `auto`. `auto` unflattens
  only when the input is a one-level object whose keys already look like paths,
  and flattens otherwise.
- **Max depth** — `0` (default) flattens everything. With `2`,
  `{"a":{"b":{"c":1}}}` becomes `{"a.b":{"c":1}}` and anything deeper stays as a
  nested JSON value.
- **Expand arrays** — turn it off to keep every array whole as one JSON value
  while still flattening objects, which is what you want when a list is a single
  logical cell.
- **Keep empty objects and arrays** — on by default, so `{}` and `[]` survive as
  leaf entries and round-trip. Turn it off to drop those keys.
- **Key case** — `upper` gives ENV-style keys, `lower` gives SQL-style column
  keys, `preserve` (default) keeps the source spelling.

### Limits & edge cases

- **Key collisions are an error, not a silent merge.** If a source key already
  contains the separator, two different paths can flatten to the same key — the
  run fails and names the key so you can pick another separator. The same applies
  when `upper`/`lower` casing merges two keys that differed only in case.
- **Unflatten refuses conflicting paths.** Supplying both `a` and `a.b` errors
  instead of overwriting whichever came second.
- **Bracket vs dotted indices change the round-trip.** With `separator` notation
  an all-digit segment rebuilds an **array**; with `bracket` notation it stays an
  object key literally named `"0"`. Use the same setting in both directions.
- **Upper/lower key case is lossy** — the original key spelling can't be
  recovered by unflattening.
- **Non-JSON output formats are flatten-only.** Unflattening always returns
  nested JSON, so `pairs`/`csv`/`paths` error rather than being ignored.
- Input is capped at **5 MB**, **100 levels** of nesting, **200,000** flattened
  keys, and array indices up to **100,000** (so a typo like `a[999999999]` errors
  instead of allocating a huge array).
- A top-level array flattens too: `[{"a":1}]` gives `{"[0].a":1}`.

## FAQ

<details>
<summary>What's the difference between bracket and dotted array indices?</summary>

`bracket` writes `tags[0]`, `separator` writes `tags.0` using whatever separator
you chose. Bracket is the default because it is unambiguous: `[0]` can only mean
an array element, so a bare numeric segment is free to mean an object key
literally named `"0"`. With `separator` notation the rule flips — an all-digit
segment rebuilds an array. Both round-trip cleanly as long as you flatten and
unflatten with the **same** setting.

</details>

<details>
<summary>Can I get the nested JSON back after flattening?</summary>

Yes — that's what `direction = unflatten` does. Paste the flat map of paths back
in and the nested document is rebuilt, with key order preserved. The round-trip
is lossless for every option except `key case = upper`/`lower` (which discards
the original spelling) and a `max depth` cap combined with dropping empty
containers.

</details>

<details>
<summary>Why did I get a "two different paths flatten to the same key" error?</summary>

Because a key in your document already contains the separator. For example
`{"a.b":1,"a":{"b":2}}` produces the path `a.b` twice — one from the literal key,
one from the nested object. Rather than silently dropping a value, the tool stops
and names the key. Pick a separator that doesn't appear in your keys (`/`, `::`
and `__` are common choices).

</details>

<details>
<summary>How do I turn a nested API response into a spreadsheet?</summary>

Set **output** to `csv`. You get a two-column `key,value` sheet with a header
row, correctly quoted for values containing commas, quotes or newlines — paste it
straight into a spreadsheet. If you'd rather have an env file or a `.properties`
style dump, use `pairs`, which prints `path=value` lines with strings unquoted.

</details>

<details>
<summary>What happens to empty objects, arrays, and nulls?</summary>

`null` is an ordinary value and always survives as a leaf. Empty objects and
arrays have nothing inside to make a path from, so they're controlled by **keep
empty objects and arrays**: on (the default) they're emitted as `{}` / `[]` leaf
entries so the key survives the round-trip; off, those keys are dropped entirely.

</details>

<details>
<summary>Is my JSON uploaded anywhere?</summary>

No. The whole tool is compiled to WebAssembly and runs inside your browser tab,
so the document never leaves your device. That makes it safe for config files,
API responses, and anything else you wouldn't paste into a server-side
converter.

</details>

## Related tools

- [GeoJSON to CSV Converter](https://gizza.ai/tools/geojson-to-csv/): Flatten GeoJSON features into CSV rows with property columns, WKT or longitude/latitude geometry, nested-field flattening, and delimiter options.
- [JSON Path Editor](https://gizza.ai/tools/json-path-edit/): Get, set, or delete a value at a dotted or bracketed path in a JSON document. Supports array indices, quoted keys, and auto-created intermediates.
- [Query String Parser](https://gizza.ai/tools/parse-query-string/): Parse a URL query string into key/value pairs and structured JSON — repeated keys, PHP/Rails bracket notation, percent and + decoding. Free, in your browser.
- [Absolute value, sign, or negation for a whole column](https://gizza.ai/tools/absolute-value-transformer/): Paste a column of numbers and apply absolute value, sign extraction (-1/0/1), sign flipping, or force-negative to every value at once, with rounding and an audit table.
- [Adjacency Matrix Converter](https://gizza.ai/tools/adjacency-matrix-converter/): Convert a graph between edge list, adjacency matrix, and incidence matrix — directed or undirected, weighted or not. Free, private, runs in your browser.
