# NDJSON Viewer

Paste NDJSON / JSON Lines and read it: pretty, compact, array or table view, search any key or value at any depth, and per-line error reporting.

## Run it

- **CLI:** `gizza tool ndjson-viewer '{"id":1,"status":"ok","latency_ms":12}
{"id":2,"status":"error","latency_ms":940,"err":{"code":"timeout"}}
{"id":3,"status":"ok","latency_ms":31}'`
- **Web:** https://gizza.ai/tools/ndjson-viewer/
- **Agents:** machine-readable descriptor (parameters JSON Schema) at https://gizza.ai/tools/ndjson-viewer/tool.json

## Inputs

- `data` — NDJSON / JSON Lines (one complete JSON value per line) _(field)_
- `view` — Show the records as _(field)_
- `indent` — Indent (spaces, 0 minifies) _(field)_
- `search` — Search any key or value, at any depth _(field)_
- `search_in` — Search matches _(field)_
- `case_sensitive` — Case-sensitive matching _(field)_
- `path` — Filter by dotted path (numbers index arrays) _(field)_
- `value` — …and require this value there _(field)_
- `match_mode` — Compare search and value by _(field)_
- `sort_keys` — Sort every object's keys alphabetically _(field)_
- `skip` — Skip this many matching records _(field)_
- `limit` — Show at most this many records (0 = all) _(field)_
- `invalid` — Lines that are not valid JSON _(field)_
- `line_numbers` — Show input line numbers _(field)_
- `stats` — Prepend a stats header (# comment lines) _(field)_

## Output

- Records (text)

## Query parameters

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

- `data` — NDJSON / JSON Lines (one complete JSON value per line)
- `view` — Show the records as
- `indent` — Indent (spaces, 0 minifies)
- `search` — Search any key or value, at any depth
- `search_in` — Search matches
- `case_sensitive` — Case-sensitive matching
- `path` — Filter by dotted path (numbers index arrays)
- `value` — …and require this value there
- `match_mode` — Compare search and value by
- `sort_keys` — Sort every object's keys alphabetically
- `skip` — Skip this many matching records
- `limit` — Show at most this many records (0 = all)
- `invalid` — Lines that are not valid JSON
- `line_numbers` — Show input line numbers
- `stats` — Prepend a stats header (# comment lines)

Example: `https://gizza.ai/tools/ndjson-viewer/?data=%7B%22id%22%3A1%2C%22status%22%3A%22ok%22%2C%22latency_ms%22%3A12%7D%0A%7B%22id%22%3A2%2C%22status%22%3A%22error%22%2C%22latency_ms%22%3A940%2C%22err%22%3A%7B%22code%22%3A%22timeout%22%7D%7D%0A%7B%22id%22%3A3%2C%22status%22%3A%22ok%22%2C%22latency_ms%22%3A31%7D&view=pretty&indent=2&search=timeout&search_in=any&case_sensitive=true&path=err.code&value=error&match_mode=contains&sort_keys=true&skip=0&limit=0&invalid=report&line_numbers=true&stats=true`

---

## About this tool

NDJSON (also called JSON Lines) is the format many loggers, queues and export jobs use when they
want one JSON record per line. It is friendly to streaming tools, but not friendly to eyeballs: one
broken line can hide inside thousands of records, nested fields are hard to scan, and turning the
stream into an array or a table usually means writing a one-off script.

This viewer keeps the streaming shape but makes it readable. Paste a stream and choose one of four
views:

| View | Use it when |
|---|---|
| Pretty | You want one indented record per block, with key order preserved. |
| Compact | You want valid NDJSON back, minified one record per line. |
| Array | A downstream tool accepts JSON arrays but not JSON Lines. |
| Table | Records are mostly objects and you want a quick column view. |

Search walks every key and scalar value at any depth, so `timeout` finds `err.code` without you
knowing the path. When you do know the path, use a dotted selector like `user.name` or
`items.0.id`. Invalid lines are reported in place by default, with line and column, so you can fix
the bad record without losing the rest of the stream.

### Worked example

Paste this stream:

```json
{"id":1,"status":"ok","latency_ms":12}
{"id":2,"status":"error","latency_ms":940,"err":{"code":"timeout"}}
{"id":3,"status":"ok","latency_ms":31}
```

Set **Search any key or value** to `timeout`, turn on **Show input line numbers**, and leave the
view on **Pretty**. The result is only the matching record, with the original line number preserved:

```json
# record 1 (line 2)
{
  "id": 2,
  "status": "error",
  "latency_ms": 940,
  "err": {
    "code": "timeout"
  }
}
```

### Limits and edge cases

- Up to **50,000 non-blank lines** per run.
- Every non-blank line must be one complete JSON value. Pretty-printed multi-line JSON is JSON, not
  NDJSON; use the array view after first converting it to one record per line.
- Table view is a quick text table, not CSV. Nested objects and arrays are rendered as compact JSON
  inside a cell.
- Search is text-based over keys and scalar values. For a boolean expression language or field
  projection, use a dedicated NDJSON filter tool instead.

## FAQ

<details>
<summary>What is the difference between NDJSON and a JSON array?</summary>

NDJSON has one complete JSON value per line, with no comma between lines and no outer brackets. It
is easy to append to and stream because a program can parse each line as soon as it arrives. A JSON
array wraps all records in `[` and `]` with commas between them, which is what many web APIs and
editors expect. The **Array** view turns the lines into that shape without changing the records.

</details>

<details>
<summary>Will one bad line make the whole stream fail?</summary>

Not by default. **Lines that are not valid JSON** is set to **Report each one in place**, so the
viewer prints a `# line N: invalid JSON` marker and keeps rendering the other records. Switch it to
**Stop at the first one** when you are validating a machine pipeline and want a non-zero failure
instead, or **Drop them silently** when you are exploring a noisy capture.

</details>

<details>
<summary>How do dotted paths work?</summary>

A path segment walks an object key, and a numeric segment indexes an array. `user.name` reads the
`name` key inside the `user` object. `items.0.id` reads the `id` key from the first item in the
`items` array. If the path is present and the value box is empty, the path is an existence filter;
if the value box is filled, that exact field is compared using the selected match mode.

</details>

<details>
<summary>Does table view flatten nested JSON?</summary>

No. The table unions top-level keys across the shown object records. If a cell is a nested object or
array, that nested value is printed as compact JSON inside the cell. That keeps the table faithful
and predictable; deep flattening needs choices about separators, arrays and duplicate keys that are
better handled by a converter built for tabular output.

</details>

## Related tools

- [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.
- [Amazon Order Analyzer](https://gizza.ai/tools/amazon-order-analyzer/): Paste an Amazon order-history CSV export to summarize total spend by month, top items, and category breakdowns. Browser-only, private, with Markdown or JSON output.
- [ARFF Converter](https://gizza.ai/tools/arff-converter/): Convert Weka ARFF datasets to CSV and CSV tables back to ARFF locally — nominal attributes, numeric types, dates, sparse rows, missing values, and type rows.
- [Avro to JSON Converter](https://gizza.ai/tools/avro-to-json/): Decode Apache Avro Object Container Files (.avro / OCF) to JSON, NDJSON, or the embedded schema — no .avsc needed, free and private in your browser.
