Skip to main content

jsonl-diff

PyPI Python CI Coverage License

Compare large JSONL/NDJSON datasets by record identity instead of line position, without loading the complete inputs into memory.

jsonl-diff matches records using one or more top-level fields and reports:

  • equal
  • added
  • deleted
  • modified

It is useful for snapshots, ETL validation, migrations, exports, and CI checks.

For line-level, field-level, JSON Patch, or visual diffs, use a tool designed for those purposes instead.

Installation

Python 3.8–3.14.

python -m pip install jsonl-diff

Quick start

Given:

{"id":1,"name":"Ada"}
{"id":2,"name":"Grace"}

and:

{"id":2,"name":"Grace Hopper"}
{"name":"Ada","id":1}
{"id":3,"name":"Linus"}

compare them by id:

jsonl-diff old.jsonl new.jsonl --key id
Records:
  equal:     1
  added:     1
  deleted:   0
  modified:  1

Record order does not matter.

Features

  • Single or composite top-level identities.
  • Disk-backed comparison for large datasets (architecture).
  • Optional JMESPath filtering with --where.
  • Configurable duplicate handling: error, first, or last.
  • Exact RFC 6901 JSON Pointer ignores.
  • Semantic number comparison using Decimal.
  • Deterministic summaries and change iteration.
  • Original OLD/NEW physical line numbers.
  • Machine-readable JSONL change log with --details (format).
  • Local, HTTP/HTTPS, file-like, and supported compressed sources.
  • CLI and Python API using the same comparison engine.

CLI

jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
           [--duplicates {error,first,last}] [--details FILE] [--quiet]
           [--max-temp MAX_TEMP]
           old new
Option Description
old, new Local path, HTTP/HTTPS source, or - for stdin
--key KEY Required top-level identity field; repeat or comma-separate for composite keys
--ignore POINTER RFC 6901 pointer to exclude from content comparison
--where EXPRESSION JMESPath filter applied to each record
--duplicates POLICY error (default), first, or last
--details FILE Write deterministic machine-readable JSONL changes
--quiet Suppress the normal summary
--max-temp BYTES Limit jsonl-diff temporary storage

Examples:

# Composite identity
jsonl-diff old.jsonl new.jsonl --key country,customerId,type

# Ignore volatile fields
jsonl-diff old.jsonl new.jsonl \
  --key id \
  --ignore /updated_at \
  --ignore /metadata/request_id

# Filter records
jsonl-diff old.jsonl new.jsonl \
  --key id \
  --where 'deleted_at == `null`'

# Compressed input
jsonl-diff old.jsonl.xz new.jsonl.gz --key id

Exit codes

Code Meaning
0 Inputs are equal
1 Added, deleted, or modified records found
2 Input, resource, output, or runtime error
3 Invalid CLI configuration or usage

Python API

from jsonl_diff import ChangeOperation, diff

with diff(
    "old.jsonl.gz",
    "new.jsonl.gz",
    key=("country", "customerId"),
    ignore=("/updated_at",),
    where='country == `"ES"`',
) as result:
    print(result.summary)

    for change in result.changes(ChangeOperation.MODIFIED):
        print(change.key, change.old_line, change.new_line)

diff() returns a disk-backed DiffResult, used as a context manager. Results are streamed lazily through changes() rather than materialized in memory.

The main result models are:

Summary(equal=10, added=2, deleted=1, modified=3)

and immutable Change objects containing the operation, typed identity, and OLD/NEW source locations.

The public exception hierarchy is rooted at JsonlDiffError:

  • ConfigurationError
  • InputError
  • DuplicateKeyError
  • ResourceError

See the Python API reference for the full callable signature, result models, Decimal key semantics, and error hierarchy.

Comparison semantics

jsonl-diff is intentionally strict:

  • Every input line must contain exactly one valid top-level JSON object.
  • Blank lines, malformed JSON, duplicate properties, NaN, and infinities are rejected.
  • Identity fields must be top-level scalar values (null allowed only as one component of a composite key).
  • Identity types remain significant: "1"1, true1.
  • Duplicate identities fail by default; --duplicates first/last tolerate them.
  • Object property order is ignored; array order is significant.
  • Numbers are compared by mathematical value: 1, 1.0, and 1e0 are equal.
  • Unicode strings are compared without normalization.
  • Changes are reported in deterministic identity order.

--where selects which records participate; --key defines identity; --ignore removes fields from content comparison. See Comparison semantics for the full rules, including duplicate handling, ignore-pointer edge cases, --where evaluation order, and canonical number formatting.

Sources & compression

Supported sources include local paths and HTTP/HTTPS URLs. The Python API also accepts file-like objects.

Supported compression:

  • gzip
  • bzip2
  • xz
  • Zstandard on Python 3.14

See architecture for the full source/compression support matrix and how sources are delegated to py-jsonl.

Limitations

jsonl-diff does not provide:

  • line/position-based diffs
  • field-level diffs or JSON Patch
  • move/rename detection
  • nested identities or automatic key detection
  • fuzzy matching or numeric tolerances
  • unordered-array comparison
  • schema validation or input repair
  • ZIP, database, or cloud-provider inputs
  • GUI or HTML reports

It is a dataset reconciliation tool, not a general-purpose visual JSON diff.

Further reading

Development

uv sync --group test --group lint

uv run pytest
uv run ruff check --quiet --output-format=concise .

The test suite covers CLI behavior, identity/canonicalization, validation, details output, sources, compression, temporary limits, and cleanup.

License

See LICENSE.

Release files for jsonl-diff 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jsonl-diff 0.1.1
File Size Uploaded
jsonl_diff-0.1.1.tar.gz 21.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jsonl-diff 0.1.1
File Interpreter ABI Platform
jsonl_diff-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 34.9 kB

Release files / jsonl_diff-0.1.1.tar.gz

Download URL jsonl_diff-0.1.1.tar.gz
Size 21.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2ebadd8a2271db0c1cb734a7749474c93752169c11c2da84cbda3b425d24b6ea
BLAKE2b-256 checksum
How to use checksums
8b2d2691d9fcf1fbd352f69066216a7f5155fea8fbbe83708d8996d61bfb69b8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / jsonl_diff-0.1.1-py3-none-any.whl

Download URL jsonl_diff-0.1.1-py3-none-any.whl
Size 13.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
80c98567eafd665d056a31f0970f30968f42a58b07fca381e8d056fc16f91d2e
BLAKE2b-256 checksum
How to use checksums
75a1677dd3761258a593381a88e282559c1e4b47afdf442bd7f5250b79c64893
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page