# How the JSON Output Mode in witr Handles Multiple Input Sources

> Learn how witr's JSON output mode efficiently handles multiple input sources by aggregating results and errors into a single JSON array for seamless processing.

- Repository: [Pranshu Parmar/witr](https://github.com/pranshuparmar/witr)
- Tags: deep-dive
- Published: 2026-08-10

---

**When using the `--json` flag with multiple inputs, witr aggregates all results into a single JSON array, including both successful process lookups and error entries for failed targets.**

The `pranshuparmar/witr` CLI tool provides a machine-readable JSON output mode designed for scripting and integration workflows. Understanding how this mode behaves with multiple input sources requires examining the core aggregation logic in the application layer and the serialization utilities in the output package.

## Single-Target vs. Multi-Target JSON Processing

witr distinguishes between single and multiple input sources to determine output formatting strategy.

### Single-Target JSON Output

For a single input source, witr serializes the `model.Result` struct immediately using dedicated helper functions. The [`internal/output/json.go`](https://github.com/pranshuparmar/witr/blob/main/internal/output/json.go) file defines multiple serialization formats:

- **`ToJSON`** – Full process details including ancestry and children
- **`ToShortJSON`** – Compact format with essential fields only  
- **`ToTreeJSON`** – Hierarchical tree representation
- **`ToWarningsJSON`** – Warning-focused output
- **`ToEnvJSON`** – Environment variable inspection

These helpers write directly to standard output without buffering.

### Multi-Target JSON Aggregation

When multiple sources are provided, the behavior shifts fundamentally. In [`internal/app/app.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go), witr accumulates results in a slice variable named `jsonResults` rather than emitting each result immediately.

The aggregation flow follows this pattern:

1. Initialize empty `jsonResults` slice
2. For each input target, perform lookup and serialization
3. Append serialized JSON string to `jsonResults`
4. After all targets processed, marshal complete slice as JSON array
5. Write final array to standard output in single operation

This design ensures valid, parseable JSON output regardless of how many sources succeed or fail.

## Error Handling in Multi-Target JSON Mode

Failed lookups do not terminate execution or corrupt the JSON structure. The `jsonErrorEntry` function in [`internal/app/app.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go) generates standardized error objects:

```go
// Error entry format consistent with successful results
{"error": "descriptive message", "target": "input specification"}

```

These error entries interleave with successful results in the final array, preserving target order and enabling callers to correlate errors with specific inputs.

## JSON Structure and Field Behavior

The underlying data model in `internal/model` defines the `Result` struct with `omitempty` JSON tags. Key characteristics include:

- **Consistent schema** – All entries share the same top-level structure
- **Sparse fields** – Empty arrays and zero values are omitted (e.g., `Children` field absent when no child processes exist)
- **Deterministic ordering** – Array preserves input target sequence

## Practical Usage Examples

### Single Process Inspection

```bash
witr --json pid 1234

```

Output:

```json
{"PID":1234,"Command":"nginx","Ancestry":[{"PID":1,"Command":"systemd"}],"Children":[]}

```

### Multiple Port Inspection with Mixed Results

```bash
witr --json port 8080 port 5432 port 9999

```

Output:

```json
[
  {"Port":8080,"PID":5678,"Command":"node","Ancestry":[...]},
  {"Port":5432,"PID":9012,"Command":"postgres","Ancestry":[...]},
  {"error":"no process listening on port 9999","target":"port 9999"}
]

```

### Programmatic Processing with jq

```bash
witr --json pid $(pgrep -d',' -f "python") | jq '.[] | select(.Command | contains("worker"))'

```

## Source Code Reference

| File | Purpose | GitHub URL |
|------|---------|------------|
| [`internal/app/app.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go) | CLI entry point, `--json` flag handling, `jsonResults` aggregation, `jsonErrorEntry` | <https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go> |
| [`internal/output/json.go`](https://github.com/pranshuparmar/witr/blob/main/internal/output/json.go) | JSON serialization helpers: `ToJSON`, `ToShortJSON`, `ToTreeJSON`, `ToWarningsJSON`, `ToEnvJSON` | <https://github.com/pranshuparmar/witr/blob/main/internal/output/json.go> |
| [`internal/app/render_test.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/render_test.go) | Test coverage for JSON output formatting and multi-target accumulation | <https://github.com/pranshuparmar/witr/blob/main/internal/app/render_test.go> |
| [`internal/app/collect_test.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/collect_test.go) | Verification of error entry generation and inclusion in JSON arrays | <https://github.com/pranshuparmar/witr/blob/main/internal/app/collect_test.go> |
| [`internal/model/result.go`](https://github.com/pranshuparmar/witr/blob/main/internal/model/result.go) | `Result` struct definition with JSON tags | <https://github.com/pranshuparmar/witr/blob/main/internal/model/result.go> |

## Performance Characteristics

The multi-target JSON mode maintains **O(n)** memory complexity relative to input count, as all results are buffered in `jsonResults` before final output. For extremely large target lists (thousands of PIDs or ports), consider:

- Piping through `witr` in batches
- Using `--short` flag to reduce per-entry serialization overhead
- Filtering targets at the shell level before passing to witr

## Summary

- **Single targets** serialize immediately via [`internal/output/json.go`](https://github.com/pranshuparmar/witr/blob/main/internal/output/json.go) helpers
- **Multiple targets** aggregate in `jsonResults` slice and emit as unified JSON array
- **Errors return as structured objects** via `jsonErrorEntry`, maintaining parseable output
- **`omitempty` tags** produce compact JSON by excluding empty fields
- **Source order preservation** enables reliable result-to-input correlation

## Frequently Asked Questions

### What happens if one target fails when using JSON mode with multiple inputs?

witr continues processing remaining targets. The failure is recorded as a JSON error entry in the final array using `jsonErrorEntry`, allowing the complete output to parse successfully while indicating which specific input failed.

### Does the JSON output mode support streaming for large target lists?

No. As implemented in [`internal/app/app.go`](https://github.com/pranshuparmar/witr/blob/main/internal/app/app.go), witr buffers all `jsonResults` in memory before the final write. The output is emitted as a single valid JSON array after all targets complete, trading memory usage for guaranteed structural validity.

### How do I distinguish between different result types in the JSON array?

Each object contains identifying fields based on lookup type: `PID` for process ID lookups, `Port` for network port inspections, `Label` for container labels, or `error` for failed operations. The presence of these fields serves as implicit type discrimination.

### Can I combine JSON output with other formatting flags like `--short` or `--tree`?

Yes. The `--json` flag accepts modifier flags that route to specific serializers: `--short` uses `ToShortJSON`, and `--tree` uses `ToTreeJSON`. These produce different JSON schemas optimized for specific use cases while maintaining the same multi-target aggregation behavior.