# JSON entity normalizer

Normalize deeply nested JSON into id-keyed entity tables plus a result id, using a compact entity schema and deterministic local WASM.

## Run it

- **CLI:** `gizza tool json-normalize '{"id":"123","title":"My first post!","author":{"id":"1","name":"Paul"},"comments":[{"id":"324","commenter":{"id":"2","name":"Nicole"}}]}' 'schema=articles: author -> users, comments -> [comments]
comments: commenter -> users
users:' 'root=articles'`
- **Web:** https://gizza.ai/tools/json-normalize/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/json-normalize/tool.json

## Inputs

- `json` — JSON document _(field)_
- `schema` — Entity schema _(field)_
- `root` — Root entity _(field)_
- `path` — Payload path (optional) _(field)_
- `id_field` — ID field(s) _(field)_
- `on_missing_id` — Missing ID _(field)_
- `on_conflict` — Duplicate IDs _(field)_
- `output` — Output _(field)_
- `pretty` — Pretty-print JSON _(field)_
- `indent` — Indent spaces _(field)_

## Output

- Normalized JSON (text)

## Query parameters

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

- `json` — JSON document
- `schema` — Entity schema
- `root` — Root entity
- `path` — Payload path (optional)
- `id_field` — ID field(s)
- `on_missing_id` — Missing ID
- `on_conflict` — Duplicate IDs
- `output` — Output
- `pretty` — Pretty-print JSON
- `indent` — Indent spaces

Example: `https://gizza.ai/tools/json-normalize/?json=%7B%22id%22%3A%22123%22%2C%22title%22%3A%22My%20first%20post%21%22%2C%22author%22%3A%7B%22id%22%3A%221%22%2C%22name%22%3A%22Paul%22%7D%2C%22comments%22%3A%5B%7B%22id%22%3A%22324%22%2C%22commenter%22%3A%7B%22id%22%3A%222%22%2C%22name%22%3A%22Nicole%22%7D%7D%5D%7D&schema=articles%3A%20author%20-%3E%20users%2C%20comments%20-%3E%20%5Bcomments%5D%0Acomments%3A%20commenter%20-%3E%20users%0Ausers%3A&root=articles&path=data.items&id_field=id%2C_id%2Cuuid&on_missing_id=error&on_conflict=merge&output=normalized&pretty=true&indent=2`

---

## About this tool

JSON normalization turns a nested document into lookup tables keyed by id. Instead of keeping the same `user` object repeated inside every post and comment, the output stores one `entities.users["1"]` record and replaces each nested occurrence with the id `"1"`. This is the same shape many Redux, cache, and ETL pipelines expect: `{ "entities": { ... }, "result": ... }`.

Describe the relationships with a compact schema. JSON form uses entity names as keys, for example `{"articles":{"author":"users","comments":["comments"]},"comments":{"commenter":"users"},"users":{}}`. Shorthand form is easier to type: `articles: author -> users, comments -> [comments]`. Fields not named in the schema stay on their original entity.

Worked example: paste a post whose `author` is `{ "id": "1", "name": "Paul" }`, set root to `articles`, and use schema `articles: author -> users`. The post is stored in `entities.articles`, the author is stored in `entities.users`, and the article's `author` field becomes just `"1"`.

Limits and edge cases: the JSON document is capped at 5 MB, schemas are capped at 100 KB, nesting is capped at 100 levels, and at most 200,000 extracted entities are kept. The tool is schema-guided and deterministic; it does not run JavaScript callbacks, infer arbitrary polymorphic unions, or denormalize an entity store back into a tree.

## FAQ

<details>
<summary>Is this the same as flattening JSON into dotted keys?</summary>

No. Dotted-key flattening rewrites `{ "a": { "b": 1 } }` into something like `{ "a.b": 1 }`. Entity normalization extracts nested records into tables keyed by id and replaces nested objects with references, so repeated records are stored once.

</details>

<details>
<summary>How do I describe arrays of nested entities?</summary>

Use a one-element array in JSON schema form, such as `"comments": ["comments"]`, or brackets in shorthand form, such as `comments -> [comments]`. A single object in a list field becomes a one-element reference list so messy payloads still normalize.

</details>

<details>
<summary>What happens when an entity has no id?</summary>

The default is `error` so missing ids do not silently corrupt the store. You can choose `index` for run-local ids like `users-1`, `hash` for content-based ids, or `keep` to leave that nested object inline instead of extracting it.

</details>

<details>
<summary>Can I use custom id fields like `_id` or `id_str`?</summary>

Yes. Set `id_field` to a single field, a comma-separated fallback list such as `id,_id,uuid`, or a JSON map like `{ "*": "id", "tweets": "id_str" }` for per-entity rules.

</details>

## Related tools

- [Identify a data sample's format, delimiter and columns](https://gizza.ai/tools/data-format-sniffer/): Paste a data sample and find out what it is: CSV, TSV, JSON, JSON Lines, XML, HTML, fixed-width or binary, plus delimiter, quote char, encoding and column types.
- [DynamoDB JSON Converter](https://gizza.ai/tools/dynamodb-json-converter/): Convert between DynamoDB typed AttributeValue JSON and plain JSON in both directions, with auto-detect and pretty or compact output.
- [Elasticsearch Bulk Formatter](https://gizza.ai/tools/elasticsearch-bulk-formatter/): Build a compact Elasticsearch _bulk API body from a JSON array. Choose index/create/update/delete, _index, _id field, and doc_as_upsert locally.
- [Convert stringified JSON values back to real types](https://gizza.ai/tools/json-coerce-types/): Paste JSON where every value is quoted and get real types back: numbers, booleans and nulls. Protects ZIP codes, big integers and chosen keys.
- [Format and filter structured JSON logs](https://gizza.ai/tools/json-log-formatter/): Pretty-print JSON log lines, flatten nested fields, filter by level or field, and export readable logs as text, table, JSON, or CSV.
