Metadata-Version: 2.4
Name: jsondiffhtml
Version: 0.1.0
Summary: Diff two JSON files and produce a modern, self-contained, themeable HTML report. No runtime dependencies.
Project-URL: Homepage, https://github.com/YRamzy993/jsondiffhtml
Project-URL: Repository, https://github.com/YRamzy993/jsondiffhtml
Project-URL: Issues, https://github.com/YRamzy993/jsondiffhtml/issues
Project-URL: Changelog, https://github.com/YRamzy993/jsondiffhtml/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/YRamzy993/jsondiffhtml#readme
Author: jsondiffhtml contributors
License: MIT
License-File: LICENSE
Keywords: cli,compare,comparison,deep-diff,diff,html,html-report,json,json-diff,report,structural-diff,visual-diff,visualization
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Markup :: HTML
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest-cov; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# jsondiffhtml

**The fastest way to turn two JSON files into a shareable, reviewable HTML diff report.**

[![PyPI](https://img.shields.io/pypi/v/jsondiffhtml.svg)](https://pypi.org/project/jsondiffhtml/)
[![Python versions](https://img.shields.io/pypi/pyversions/jsondiffhtml.svg)](https://pypi.org/project/jsondiffhtml/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Zero dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen.svg)](pyproject.toml)

`jsondiffhtml` reads two JSON files, deep-diffs them (dicts, lists, primitives, and unordered lists of objects), and writes a **single, self-contained HTML report** you can email, commit, attach to a ticket, or host anywhere. No CDN calls, no external stylesheets, no runtime dependencies.

```bash
pip install jsondiffhtml
json-diff before.json after.json -o report.html
```

---

## Why jsondiffhtml?

Most Python JSON-diff libraries hand you back a Python dict. That's useful if you're writing a program — useless if you're the reviewer on a pull request, the engineer debugging a broken deploy, or the ops person approving a config change. Those people want a **diff they can read**.

| | jsondiffhtml | [deepdiff](https://pypi.org/project/deepdiff/) | [xlwings/jsondiff](https://github.com/xlwings/jsondiff) |
|---|---|---|---|
| Output format | **HTML report** (self-contained) | Python dict | Python dict |
| Grouping by JSON path | ✅ | ❌ | ❌ |
| Searchable UI with filter tabs | ✅ | ❌ | ❌ |
| Source line numbers per diff | ✅ | ❌ | ❌ |
| Per-item ignore & comment boxes | ✅ | ❌ | ❌ |
| Identifier-based list matching | ✅ | partial | partial |
| Include/exclude path filters | ✅ | ✅ | ✅ |
| Themeable (color, title, logo) | ✅ | n/a | n/a |
| Auto-splits very large reports | ✅ | n/a | n/a |
| CLI included | ✅ (`json-diff`) | ❌ | ✅ (`jdiff`) |
| Runtime dependencies | **0** | 1 (`orderly-set`) | 0 |
| Python 3.9+ | ✅ | ✅ | ✅ |

**Use jsondiffhtml when you need a human-readable diff artifact.** Use `deepdiff` or `xlwings/jsondiff` when you need a programmatic dict to drive application logic.

---

## Quick start — CLI

```bash
# minimal
json-diff before.json after.json -o report.html

# themed, with a logo in the sidebar
json-diff before.json after.json -o report.html \
  --title "Release 2.4 config diff" \
  --primary-color "#16a34a" \
  --logo-url https://example.com/logo.svg

# filter to one subtree
json-diff before.json after.json -o report.html \
  --include-path "root['services']" \
  --exclude-path "root['services']['internal']"
```

Open `report.html` in a browser. Everything is inline — you can attach it to an email, host it on a static bucket, or drop it into a Slack thread.

## Quick start — Python

```python
from jsondiffhtml import JsonDiff, HtmlReporter, ReportConfig

diff = JsonDiff.from_files("before.json", "after.json")
print(f"{len(diff.objects_changed)} changed, "
      f"{len(diff.objects_added)} added, "
      f"{len(diff.objects_removed)} removed")

HtmlReporter(
    diff,
    ReportConfig(title="Release 2.4 config diff", primary_color="#16a34a"),
).write("report.html")
```

Each item in `diff.objects_changed`, `diff.objects_added`, `diff.objects_removed` is a frozen dataclass carrying the source path, destination path, old/new values, source-line numbers, and a human-readable label (see `ListIdentifier` below).

## Matching unordered list items by identifier

When your JSON contains lists of objects whose order doesn't matter (users, resources, records), tell `jsondiffhtml` which key identifies each item so a reordered-but-unchanged list doesn't flood your report with false positives.

```python
from jsondiffhtml import JsonDiff, HtmlReporter, ListIdentifier

diff = JsonDiff(
    before,
    after,
    identifiers_list=[
        ListIdentifier(
            parent_path="root['users']",
            id_key_name="id",
            get_identifier_value_func=lambda u: f"{u['first_name']} {u['last_name']}",
        ),
    ],
)
HtmlReporter(diff).write("users_diff.html")
```

- `parent_path` is the path to the list with `[<int>]` indices stripped (`root['users']`, not `root['users'][0]`).
- `get_identifier_value_func` is optional. If set, its return value becomes the readable label shown on each diff row — so reviewers see "Jane Doe" instead of `root['users'][17]`.

## Filtering

```python
JsonDiff(
    a, b,
    included_paths=["root['config']"],              # keep only diffs whose path contains this
    excluded_paths=["root['config']['secret']"],    # drop diffs whose (index-stripped) path matches exactly
)
```

## Large reports

When the total item count exceeds `ReportConfig.split_threshold`, write multiple files instead of one massive HTML:

```python
HtmlReporter(diff, ReportConfig(split_threshold=50_000, max_items_per_file=30_000)) \
    .write_split("out_dir", "report")
# Produces: out_dir/report_changed_0.html, _added_0.html, _removed_0.html, ...
```

## ReportConfig reference

| Field | Default | Purpose |
|---|---|---|
| `title` | `"JSON Diff Report"` | Page title and sidebar heading |
| `logo_url` | `None` | Optional image shown above the title |
| `primary_color` | `"#2563eb"` | Any CSS color — drives the theme via CSS variables |
| `group_results` | `True` | `True` = group items by path; `False` = flat chronological list |
| `show_line_numbers` | `True` | Annotate paths with `(line N)` |
| `include_annotations` | `True` | Render per-item Ignore checkbox and comment textarea |
| `split_threshold` | `50_000` | When `write_split()` sees more items than this, splits per-category |
| `max_items_per_file` | `30_000` | Chunk size when splitting |

## Python API reference

```python
from jsondiffhtml import (
    JsonDiff,                      # core diff engine
    ListIdentifier,                # identifier-based list matching
    ReportConfig,                  # report customization
    HtmlReporter,                  # HTML generation
    AddedObj, ChangedObj, RemovedObj,   # result dataclasses
    JsonPathLineNumberMapper,      # path → line number lookup (used internally; public for advanced use)
)
```

`JsonDiff` constructors:

- `JsonDiff(obj_a, obj_b, identifiers_list=None, included_paths=None, excluded_paths=None)` — from already-parsed Python objects.
- `JsonDiff.from_files(path_a, path_b, **kwargs)` — load JSON from disk.

`HtmlReporter` methods:

- `render() -> str` — return the full HTML document as a string.
- `write(path) -> Path` — write a single-file report.
- `write_split(dir, basename) -> list[Path]` — auto-split on large reports.

## CLI reference

```
json-diff [-h] [-o OUTPUT] [--title TITLE] [--primary-color COLOR]
          [--logo-url URL] [--flat] [--no-annotations] [--no-line-numbers]
          [--include-path PATH] [--exclude-path PATH] [--version]
          file_a file_b
```

## Development

```bash
git clone https://github.com/OWNER/jsondiffhtml && cd jsondiffhtml
pip install -e ".[dev]"
pytest               # 32 tests
ruff check src/
mypy src/jsondiffhtml
python -m build      # wheel + sdist
```

## Changelog

See [CHANGELOG.md](CHANGELOG.md).

## License

MIT — see [LICENSE](LICENSE).
