jsonl-diff 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/PKG-INFO +48 -12
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/README.md +43 -10
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.egg-info/PKG-INFO +48 -12
- jsonl_diff-0.1.4/jsonl_diff.egg-info/requires.txt +8 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.py +301 -91
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/pyproject.toml +9 -2
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/tests/test_cli.py +70 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/tests/test_diff.py +156 -1
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/tests/test_sources.py +106 -0
- jsonl_diff-0.1.2/jsonl_diff.egg-info/requires.txt +0 -2
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/LICENSE +0 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.egg-info/SOURCES.txt +0 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.egg-info/dependency_links.txt +0 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.egg-info/entry_points.txt +0 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/jsonl_diff.egg-info/top_level.txt +0 -0
- {jsonl_diff-0.1.2 → jsonl_diff-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonl-diff
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets without loading them entirely into memory.
|
|
5
5
|
Author-email: rmoralespp <rmoralespp@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -49,8 +49,11 @@ Classifier: Topic :: File Formats :: JSON
|
|
|
49
49
|
Requires-Python: >=3.8
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
51
51
|
License-File: LICENSE
|
|
52
|
-
Requires-Dist:
|
|
52
|
+
Requires-Dist: ijson>=3.2
|
|
53
|
+
Requires-Dist: py-jsonl==1.5.3
|
|
53
54
|
Requires-Dist: jmespath>=1.0.0
|
|
55
|
+
Provides-Extra: speedups
|
|
56
|
+
Requires-Dist: msgspec>=0.18; python_version >= "3.10" and extra == "speedups"
|
|
54
57
|
Dynamic: license-file
|
|
55
58
|
|
|
56
59
|
# jsonl-diff
|
|
@@ -63,12 +66,12 @@ Dynamic: license-file
|
|
|
63
66
|
<img src="https://img.shields.io/github/license/rmoralespp/jsonl-diff.svg" alt="License">
|
|
64
67
|
</p>
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets and
|
|
70
|
+
top-level JSON arrays **by record identity instead of line position**, without
|
|
71
|
+
loading the complete inputs into memory.
|
|
68
72
|
|
|
69
73
|
`jsonl-diff` matches records using one or more top-level fields and reports:
|
|
70
|
-
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
|
|
71
|
-
source.
|
|
74
|
+
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each source.
|
|
72
75
|
|
|
73
76
|
It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
74
77
|
|
|
@@ -80,6 +83,19 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
|
80
83
|
pip install jsonl-diff
|
|
81
84
|
```
|
|
82
85
|
|
|
86
|
+
### Optional speedups
|
|
87
|
+
|
|
88
|
+
For large datasets, install the optional [`msgspec`](https://jcristharif.com/msgspec/)
|
|
89
|
+
accelerator (Python 3.10+), which uses a C parser and encoder for JSON decoding
|
|
90
|
+
and record canonicalization, speeding up large diffs by roughly **2x**:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install "jsonl-diff[speedups]"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
It is used automatically when available — no configuration or code changes. When
|
|
97
|
+
`msgspec` is absent, the pure-Python path runs instead and produces identical results.
|
|
98
|
+
|
|
83
99
|
## Quick start
|
|
84
100
|
|
|
85
101
|
Given:
|
|
@@ -119,23 +135,27 @@ Record order does not matter.
|
|
|
119
135
|
|
|
120
136
|
* Single or composite top-level identities.
|
|
121
137
|
* Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
|
|
122
|
-
* Optional **JMESPath** filtering with `--where`.
|
|
138
|
+
* Optional **[JMESPath](https://jmespath.org/)** filtering with `--where`.
|
|
123
139
|
* Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
|
|
124
|
-
* Exact **RFC 6901** JSON Pointer ignores.
|
|
140
|
+
* Exact **[RFC 6901](https://www.rfc-editor.org/info/rfc6901/)** JSON Pointer ignores.
|
|
125
141
|
* Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
|
|
126
142
|
* Semantic number comparison using `Decimal`.
|
|
143
|
+
* Optional `msgspec` accelerator for ~2x faster large diffs (`pip install "jsonl-diff[speedups]"`).
|
|
127
144
|
* Deterministic summaries and change iteration.
|
|
128
145
|
* Original OLD/NEW physical line numbers.
|
|
129
146
|
* Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
|
|
130
147
|
* Local, HTTP/HTTPS, file-like, and supported compressed sources.
|
|
148
|
+
* Incremental top-level JSON array parsing with `--format json`.
|
|
131
149
|
* CLI and Python API using the same comparison engine.
|
|
132
150
|
|
|
133
151
|
## CLI
|
|
134
152
|
|
|
135
153
|
```text
|
|
136
154
|
jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
137
|
-
[--duplicates {error,first,last}] [--
|
|
155
|
+
[--duplicates {error,first,last}] [--missing-key {error,null}]
|
|
156
|
+
[--details FILE] [--quiet]
|
|
138
157
|
[--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
|
|
158
|
+
[--format {jsonl,json}]
|
|
139
159
|
old new
|
|
140
160
|
```
|
|
141
161
|
|
|
@@ -146,11 +166,13 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
|
146
166
|
| `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
|
|
147
167
|
| `--where EXPRESSION` | JMESPath filter applied to each record |
|
|
148
168
|
| `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
|
|
169
|
+
| `--missing-key POLICY`| `error` (default), or `null` to treat an absent identity field as `null` |
|
|
149
170
|
| `--details FILE` | Write deterministic machine-readable JSONL changes |
|
|
150
171
|
| `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
|
|
151
172
|
| `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
|
|
152
173
|
| `--quiet` | Suppress the normal summary |
|
|
153
174
|
| `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
|
|
175
|
+
| `--format FORMAT` | Input format: `jsonl` (default) or a top-level JSON array with `json` |
|
|
154
176
|
|
|
155
177
|
Examples:
|
|
156
178
|
|
|
@@ -169,6 +191,11 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
169
191
|
--key id \
|
|
170
192
|
--where 'deleted_at == `null`'
|
|
171
193
|
|
|
194
|
+
# Tolerate an identity field that is absent from some records
|
|
195
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
196
|
+
--key country,customerId \
|
|
197
|
+
--missing-key null
|
|
198
|
+
|
|
172
199
|
# Detect observed schema drift
|
|
173
200
|
jsonl-diff old.jsonl new.jsonl \
|
|
174
201
|
--key id \
|
|
@@ -177,6 +204,9 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
177
204
|
|
|
178
205
|
# Compressed input
|
|
179
206
|
jsonl-diff old.jsonl.xz new.jsonl.gz --key id
|
|
207
|
+
|
|
208
|
+
# Top-level JSON arrays (parsed incrementally)
|
|
209
|
+
jsonl-diff old.json new.json --key id --format json
|
|
180
210
|
```
|
|
181
211
|
|
|
182
212
|
### Exit codes
|
|
@@ -237,8 +267,12 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
|
|
|
237
267
|
|
|
238
268
|
`jsonl-diff` is intentionally strict:
|
|
239
269
|
|
|
240
|
-
*
|
|
241
|
-
|
|
270
|
+
* In the default `jsonl` format, every input line must contain exactly one
|
|
271
|
+
valid top-level JSON object.
|
|
272
|
+
* With `--format json`, each input must be one top-level JSON array whose
|
|
273
|
+
elements are the records.
|
|
274
|
+
* Blank lines, malformed JSON, `NaN`, and infinities are rejected.
|
|
275
|
+
* Duplicate object property names within a record follow JSON's last-wins semantics: the last occurrence is kept.
|
|
242
276
|
* Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
|
|
243
277
|
* Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
|
|
244
278
|
* Duplicate identities fail by default; `--duplicates first`/`last` select one
|
|
@@ -259,7 +293,9 @@ order, and canonical number formatting.
|
|
|
259
293
|
## Sources & compression
|
|
260
294
|
|
|
261
295
|
Supported sources include local paths and HTTP/HTTPS URLs. The Python API
|
|
262
|
-
also accepts file-like objects.
|
|
296
|
+
also accepts file-like objects. JSON arrays use `py-jsonl.open_stream()` for
|
|
297
|
+
the source and `ijson` for incremental parsing, so compression and remote
|
|
298
|
+
source handling are shared with JSONL input.
|
|
263
299
|
|
|
264
300
|
Supported compression:
|
|
265
301
|
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
<img src="https://img.shields.io/github/license/rmoralespp/jsonl-diff.svg" alt="License">
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets and
|
|
12
|
+
top-level JSON arrays **by record identity instead of line position**, without
|
|
13
|
+
loading the complete inputs into memory.
|
|
13
14
|
|
|
14
15
|
`jsonl-diff` matches records using one or more top-level fields and reports:
|
|
15
|
-
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
|
|
16
|
-
source.
|
|
16
|
+
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each source.
|
|
17
17
|
|
|
18
18
|
It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
19
19
|
|
|
@@ -25,6 +25,19 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
|
25
25
|
pip install jsonl-diff
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
### Optional speedups
|
|
29
|
+
|
|
30
|
+
For large datasets, install the optional [`msgspec`](https://jcristharif.com/msgspec/)
|
|
31
|
+
accelerator (Python 3.10+), which uses a C parser and encoder for JSON decoding
|
|
32
|
+
and record canonicalization, speeding up large diffs by roughly **2x**:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install "jsonl-diff[speedups]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
It is used automatically when available — no configuration or code changes. When
|
|
39
|
+
`msgspec` is absent, the pure-Python path runs instead and produces identical results.
|
|
40
|
+
|
|
28
41
|
## Quick start
|
|
29
42
|
|
|
30
43
|
Given:
|
|
@@ -64,23 +77,27 @@ Record order does not matter.
|
|
|
64
77
|
|
|
65
78
|
* Single or composite top-level identities.
|
|
66
79
|
* Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
|
|
67
|
-
* Optional **JMESPath** filtering with `--where`.
|
|
80
|
+
* Optional **[JMESPath](https://jmespath.org/)** filtering with `--where`.
|
|
68
81
|
* Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
|
|
69
|
-
* Exact **RFC 6901** JSON Pointer ignores.
|
|
82
|
+
* Exact **[RFC 6901](https://www.rfc-editor.org/info/rfc6901/)** JSON Pointer ignores.
|
|
70
83
|
* Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
|
|
71
84
|
* Semantic number comparison using `Decimal`.
|
|
85
|
+
* Optional `msgspec` accelerator for ~2x faster large diffs (`pip install "jsonl-diff[speedups]"`).
|
|
72
86
|
* Deterministic summaries and change iteration.
|
|
73
87
|
* Original OLD/NEW physical line numbers.
|
|
74
88
|
* Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
|
|
75
89
|
* Local, HTTP/HTTPS, file-like, and supported compressed sources.
|
|
90
|
+
* Incremental top-level JSON array parsing with `--format json`.
|
|
76
91
|
* CLI and Python API using the same comparison engine.
|
|
77
92
|
|
|
78
93
|
## CLI
|
|
79
94
|
|
|
80
95
|
```text
|
|
81
96
|
jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
82
|
-
[--duplicates {error,first,last}] [--
|
|
97
|
+
[--duplicates {error,first,last}] [--missing-key {error,null}]
|
|
98
|
+
[--details FILE] [--quiet]
|
|
83
99
|
[--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
|
|
100
|
+
[--format {jsonl,json}]
|
|
84
101
|
old new
|
|
85
102
|
```
|
|
86
103
|
|
|
@@ -91,11 +108,13 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
|
91
108
|
| `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
|
|
92
109
|
| `--where EXPRESSION` | JMESPath filter applied to each record |
|
|
93
110
|
| `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
|
|
111
|
+
| `--missing-key POLICY`| `error` (default), or `null` to treat an absent identity field as `null` |
|
|
94
112
|
| `--details FILE` | Write deterministic machine-readable JSONL changes |
|
|
95
113
|
| `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
|
|
96
114
|
| `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
|
|
97
115
|
| `--quiet` | Suppress the normal summary |
|
|
98
116
|
| `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
|
|
117
|
+
| `--format FORMAT` | Input format: `jsonl` (default) or a top-level JSON array with `json` |
|
|
99
118
|
|
|
100
119
|
Examples:
|
|
101
120
|
|
|
@@ -114,6 +133,11 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
114
133
|
--key id \
|
|
115
134
|
--where 'deleted_at == `null`'
|
|
116
135
|
|
|
136
|
+
# Tolerate an identity field that is absent from some records
|
|
137
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
138
|
+
--key country,customerId \
|
|
139
|
+
--missing-key null
|
|
140
|
+
|
|
117
141
|
# Detect observed schema drift
|
|
118
142
|
jsonl-diff old.jsonl new.jsonl \
|
|
119
143
|
--key id \
|
|
@@ -122,6 +146,9 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
122
146
|
|
|
123
147
|
# Compressed input
|
|
124
148
|
jsonl-diff old.jsonl.xz new.jsonl.gz --key id
|
|
149
|
+
|
|
150
|
+
# Top-level JSON arrays (parsed incrementally)
|
|
151
|
+
jsonl-diff old.json new.json --key id --format json
|
|
125
152
|
```
|
|
126
153
|
|
|
127
154
|
### Exit codes
|
|
@@ -182,8 +209,12 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
|
|
|
182
209
|
|
|
183
210
|
`jsonl-diff` is intentionally strict:
|
|
184
211
|
|
|
185
|
-
*
|
|
186
|
-
|
|
212
|
+
* In the default `jsonl` format, every input line must contain exactly one
|
|
213
|
+
valid top-level JSON object.
|
|
214
|
+
* With `--format json`, each input must be one top-level JSON array whose
|
|
215
|
+
elements are the records.
|
|
216
|
+
* Blank lines, malformed JSON, `NaN`, and infinities are rejected.
|
|
217
|
+
* Duplicate object property names within a record follow JSON's last-wins semantics: the last occurrence is kept.
|
|
187
218
|
* Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
|
|
188
219
|
* Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
|
|
189
220
|
* Duplicate identities fail by default; `--duplicates first`/`last` select one
|
|
@@ -204,7 +235,9 @@ order, and canonical number formatting.
|
|
|
204
235
|
## Sources & compression
|
|
205
236
|
|
|
206
237
|
Supported sources include local paths and HTTP/HTTPS URLs. The Python API
|
|
207
|
-
also accepts file-like objects.
|
|
238
|
+
also accepts file-like objects. JSON arrays use `py-jsonl.open_stream()` for
|
|
239
|
+
the source and `ijson` for incremental parsing, so compression and remote
|
|
240
|
+
source handling are shared with JSONL input.
|
|
208
241
|
|
|
209
242
|
Supported compression:
|
|
210
243
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jsonl-diff
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets without loading them entirely into memory.
|
|
5
5
|
Author-email: rmoralespp <rmoralespp@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -49,8 +49,11 @@ Classifier: Topic :: File Formats :: JSON
|
|
|
49
49
|
Requires-Python: >=3.8
|
|
50
50
|
Description-Content-Type: text/markdown
|
|
51
51
|
License-File: LICENSE
|
|
52
|
-
Requires-Dist:
|
|
52
|
+
Requires-Dist: ijson>=3.2
|
|
53
|
+
Requires-Dist: py-jsonl==1.5.3
|
|
53
54
|
Requires-Dist: jmespath>=1.0.0
|
|
55
|
+
Provides-Extra: speedups
|
|
56
|
+
Requires-Dist: msgspec>=0.18; python_version >= "3.10" and extra == "speedups"
|
|
54
57
|
Dynamic: license-file
|
|
55
58
|
|
|
56
59
|
# jsonl-diff
|
|
@@ -63,12 +66,12 @@ Dynamic: license-file
|
|
|
63
66
|
<img src="https://img.shields.io/github/license/rmoralespp/jsonl-diff.svg" alt="License">
|
|
64
67
|
</p>
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
A lightweight, pure-Python tool for comparing large JSONL/NDJSON datasets and
|
|
70
|
+
top-level JSON arrays **by record identity instead of line position**, without
|
|
71
|
+
loading the complete inputs into memory.
|
|
68
72
|
|
|
69
73
|
`jsonl-diff` matches records using one or more top-level fields and reports:
|
|
70
|
-
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each
|
|
71
|
-
source.
|
|
74
|
+
`equal`, `added`, `deleted`, `modified`, and tolerated duplicates in each source.
|
|
72
75
|
|
|
73
76
|
It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
74
77
|
|
|
@@ -80,6 +83,19 @@ It is useful for snapshots, ETL validation, migrations, exports, and CI checks.
|
|
|
80
83
|
pip install jsonl-diff
|
|
81
84
|
```
|
|
82
85
|
|
|
86
|
+
### Optional speedups
|
|
87
|
+
|
|
88
|
+
For large datasets, install the optional [`msgspec`](https://jcristharif.com/msgspec/)
|
|
89
|
+
accelerator (Python 3.10+), which uses a C parser and encoder for JSON decoding
|
|
90
|
+
and record canonicalization, speeding up large diffs by roughly **2x**:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install "jsonl-diff[speedups]"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
It is used automatically when available — no configuration or code changes. When
|
|
97
|
+
`msgspec` is absent, the pure-Python path runs instead and produces identical results.
|
|
98
|
+
|
|
83
99
|
## Quick start
|
|
84
100
|
|
|
85
101
|
Given:
|
|
@@ -119,23 +135,27 @@ Record order does not matter.
|
|
|
119
135
|
|
|
120
136
|
* Single or composite top-level identities.
|
|
121
137
|
* Disk-backed comparison for large datasets ([architecture](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/architecture.md)).
|
|
122
|
-
* Optional **JMESPath** filtering with `--where`.
|
|
138
|
+
* Optional **[JMESPath](https://jmespath.org/)** filtering with `--where`.
|
|
123
139
|
* Configurable duplicate handling with counts and diagnostics: `error`, `first`, or `last`.
|
|
124
|
-
* Exact **RFC 6901** JSON Pointer ignores.
|
|
140
|
+
* Exact **[RFC 6901](https://www.rfc-editor.org/info/rfc6901/)** JSON Pointer ignores.
|
|
125
141
|
* Optional disk-backed observed-schema diff for fields, types, nullability, and requiredness.
|
|
126
142
|
* Semantic number comparison using `Decimal`.
|
|
143
|
+
* Optional `msgspec` accelerator for ~2x faster large diffs (`pip install "jsonl-diff[speedups]"`).
|
|
127
144
|
* Deterministic summaries and change iteration.
|
|
128
145
|
* Original OLD/NEW physical line numbers.
|
|
129
146
|
* Machine-readable JSONL change log with `--details` ([format](https://github.com/rmoralespp/jsonl-diff/blob/main/docs/details-format.md)).
|
|
130
147
|
* Local, HTTP/HTTPS, file-like, and supported compressed sources.
|
|
148
|
+
* Incremental top-level JSON array parsing with `--format json`.
|
|
131
149
|
* CLI and Python API using the same comparison engine.
|
|
132
150
|
|
|
133
151
|
## CLI
|
|
134
152
|
|
|
135
153
|
```text
|
|
136
154
|
jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
137
|
-
[--duplicates {error,first,last}] [--
|
|
155
|
+
[--duplicates {error,first,last}] [--missing-key {error,null}]
|
|
156
|
+
[--details FILE] [--quiet]
|
|
138
157
|
[--schema-diff] [--schema-ignore POINTER] [--max-temp MAX_TEMP]
|
|
158
|
+
[--format {jsonl,json}]
|
|
139
159
|
old new
|
|
140
160
|
```
|
|
141
161
|
|
|
@@ -146,11 +166,13 @@ jsonl-diff [-h] --key KEY [--ignore IGNORE] [--where EXPRESSION]
|
|
|
146
166
|
| `--ignore POINTER` | RFC 6901 pointer to exclude from content comparison |
|
|
147
167
|
| `--where EXPRESSION` | JMESPath filter applied to each record |
|
|
148
168
|
| `--duplicates POLICY` | `error` (default), or select and report duplicates with `first`/`last` |
|
|
169
|
+
| `--missing-key POLICY`| `error` (default), or `null` to treat an absent identity field as `null` |
|
|
149
170
|
| `--details FILE` | Write deterministic machine-readable JSONL changes |
|
|
150
171
|
| `--schema-diff` | Compare observed fields, types, nullability, and requiredness |
|
|
151
172
|
| `--schema-ignore` | RFC 6901 pointer to exclude from observed-schema profiling |
|
|
152
173
|
| `--quiet` | Suppress the normal summary |
|
|
153
174
|
| `--max-temp BYTES` | Best-effort budget for `jsonl-diff` workspace temporary storage |
|
|
175
|
+
| `--format FORMAT` | Input format: `jsonl` (default) or a top-level JSON array with `json` |
|
|
154
176
|
|
|
155
177
|
Examples:
|
|
156
178
|
|
|
@@ -169,6 +191,11 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
169
191
|
--key id \
|
|
170
192
|
--where 'deleted_at == `null`'
|
|
171
193
|
|
|
194
|
+
# Tolerate an identity field that is absent from some records
|
|
195
|
+
jsonl-diff old.jsonl new.jsonl \
|
|
196
|
+
--key country,customerId \
|
|
197
|
+
--missing-key null
|
|
198
|
+
|
|
172
199
|
# Detect observed schema drift
|
|
173
200
|
jsonl-diff old.jsonl new.jsonl \
|
|
174
201
|
--key id \
|
|
@@ -177,6 +204,9 @@ jsonl-diff old.jsonl new.jsonl \
|
|
|
177
204
|
|
|
178
205
|
# Compressed input
|
|
179
206
|
jsonl-diff old.jsonl.xz new.jsonl.gz --key id
|
|
207
|
+
|
|
208
|
+
# Top-level JSON arrays (parsed incrementally)
|
|
209
|
+
jsonl-diff old.json new.json --key id --format json
|
|
180
210
|
```
|
|
181
211
|
|
|
182
212
|
### Exit codes
|
|
@@ -237,8 +267,12 @@ signature, result models, `Decimal` key semantics, and error hierarchy.
|
|
|
237
267
|
|
|
238
268
|
`jsonl-diff` is intentionally strict:
|
|
239
269
|
|
|
240
|
-
*
|
|
241
|
-
|
|
270
|
+
* In the default `jsonl` format, every input line must contain exactly one
|
|
271
|
+
valid top-level JSON object.
|
|
272
|
+
* With `--format json`, each input must be one top-level JSON array whose
|
|
273
|
+
elements are the records.
|
|
274
|
+
* Blank lines, malformed JSON, `NaN`, and infinities are rejected.
|
|
275
|
+
* Duplicate object property names within a record follow JSON's last-wins semantics: the last occurrence is kept.
|
|
242
276
|
* Identity fields must be top-level scalar values (`null` allowed only as one component of a composite key).
|
|
243
277
|
* Identity types remain significant: `"1"` ≠ `1`, `true` ≠ `1`.
|
|
244
278
|
* Duplicate identities fail by default; `--duplicates first`/`last` select one
|
|
@@ -259,7 +293,9 @@ order, and canonical number formatting.
|
|
|
259
293
|
## Sources & compression
|
|
260
294
|
|
|
261
295
|
Supported sources include local paths and HTTP/HTTPS URLs. The Python API
|
|
262
|
-
also accepts file-like objects.
|
|
296
|
+
also accepts file-like objects. JSON arrays use `py-jsonl.open_stream()` for
|
|
297
|
+
the source and `ijson` for incremental parsing, so compression and remote
|
|
298
|
+
source handling are shared with JSONL input.
|
|
263
299
|
|
|
264
300
|
Supported compression:
|
|
265
301
|
|