json-repair 0.13.0 → 0.15.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/CLAUDE.md +1 -1
- data/lib/json/repair/version.rb +1 -1
- data/lib/json/repairer.rb +46 -4
- data/sig/json/repairer.rbs +3 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc8c6dfaa0c00f4f8dcf62be1f5bd925fe1af4b0649a7986bf4354f85f1d27bd
|
|
4
|
+
data.tar.gz: dd91d930dfe19c7796e02898202e51b9cd573b0af51c632c975689a2882e2851
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5db30ce96f442ae57ff311a93481f9c991be8b3b13e45042d9abdd6a9f0562e0a43ef2eafaf40095528104f59bee7e19753b03e4376ac170071d83f66fe1c2ca
|
|
7
|
+
data.tar.gz: 3a4129cc0b8faefa3ded17ea4f3440d623bcc7972444dd2cd2d0a2f0884fcb1059017059599962370b44e2677a45e2ad8862bdf7174cc99487ff9566745fd010
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
### 2026-06-17 (0.15.0)
|
|
4
|
+
|
|
5
|
+
* Repair elided empty array slots: `[1,,2]` → `[1,2]`, `[1,,,,2]` →
|
|
6
|
+
`[1,2]`, `[1, ,2]` → `[1,2]`, and `[,,1]` → `[1]`. A comma with no
|
|
7
|
+
value between it and the previous one (or the opening bracket) marks
|
|
8
|
+
an empty slot, which is dropped — uniformly extending the existing
|
|
9
|
+
single-leading-comma repair (`[,1]` → `[1]`) to repeated and interior
|
|
10
|
+
positions, and consistent with `dirty-json`. Divergence from upstream
|
|
11
|
+
[jsonrepair](https://github.com/josdejong/jsonrepair), which as of
|
|
12
|
+
v3.14.0 mangles `[1,,2]`/`[,,1]` into `[[1],2]`/`[[],1]` (its root
|
|
13
|
+
comma-sequence wrap misfires) and raises on `[1,,,,2]`; commented at
|
|
14
|
+
the site. Arrays only: an elided comma in an object
|
|
15
|
+
(`{"a":1,,"b":2}`) still raises rather than guessing a missing key —
|
|
16
|
+
there is no data to mangle, so the clean error is kept (and pinned in
|
|
17
|
+
the spec). Differential vs main over a comma grid: every change is an
|
|
18
|
+
array with elided commas going from mangle/raise to a correct drop,
|
|
19
|
+
with objects and all non-elided shapes unchanged. Benchmarks flat.
|
|
20
|
+
|
|
21
|
+
### 2026-06-17 (0.14.0)
|
|
22
|
+
|
|
23
|
+
* Repair a leading `+` on numbers, like in JSON5: `+1.23` → `1.23`,
|
|
24
|
+
`[+1]` → `[1]`, `{"a": +5}` → `{"a":5}`, `+.5` → `0.5`. The `+` is
|
|
25
|
+
consumed and dropped (JSON has no explicit plus), so a leading-zero
|
|
26
|
+
number is quoted exactly like its unsigned form (`+05` → `"05"`),
|
|
27
|
+
while a `+` inside an exponent is left untouched (`2e+5` →
|
|
28
|
+
`200000.0`). The `+` must be followed by a digit or a leading dot, so
|
|
29
|
+
a bare `+` (and `+abc`, `++1`, `+e5`) still raises — and requiring a
|
|
30
|
+
digit/dot keeps the 0.12.0 empty-mantissa exponent guard intact.
|
|
31
|
+
Divergence from upstream
|
|
32
|
+
[jsonrepair](https://github.com/josdejong/jsonrepair) (v3.14.0 leaves
|
|
33
|
+
`+1.23` unrepaired, raising "Unexpected end of json string"),
|
|
34
|
+
commented at the site. Mirrors the leading-dot number repair (0.9.0).
|
|
35
|
+
|
|
3
36
|
### 2026-06-12 (0.13.0)
|
|
4
37
|
|
|
5
38
|
* Repair `#` hash line comments, like in Python, YAML, or Hjson:
|
data/CLAUDE.md
CHANGED
|
@@ -24,7 +24,7 @@ A few repair heuristics deliberately go **beyond** upstream (leading-dot numbers
|
|
|
24
24
|
|
|
25
25
|
### Entry point
|
|
26
26
|
|
|
27
|
-
`JSON.repair(str)` in `lib/json/repair.rb` first tries stdlib `JSON.parse` (fast path; opt out with `skip_json_loads: true`), and falls back to `JSON::Repairer.new(str).repair` when that raises. Either way the result is re-serialized with `JSON.generate`, so **output is canonical** — whitespace collapsed, numbers normalized, duplicate keys last-write-wins — and both paths agree on it.
|
|
27
|
+
`JSON.repair(str)` in `lib/json/repair.rb` first tries stdlib `JSON.parse` (fast path; opt out with `skip_json_loads: true`), and falls back to `JSON::Repairer.new(str).repair` when that raises. Either way the result is re-serialized with `JSON.generate`, so **output is canonical** — whitespace collapsed, numbers normalized, duplicate keys last-write-wins — and both paths agree on it. There is no pre-filter regex: the fast path simply runs `JSON.parse` and falls back to the Repairer on any `JSON::ParserError`. Comments are handled either way — the bundled `json` gem currently accepts `//` and `/*` comments (an implementation detail, not part of the `JSON.parse` contract) and `JSON.generate` drops them (`{} // c` → `{}`); were a future `json` release to reject them, they would simply raise and route to the Repairer, which strips comments too. Invalid escapes, NDJSON, Markdown fences, and trailing prose already make `JSON.parse` raise and so route to the Repairer. `return_objects: true` returns the parsed Ruby value instead of a string; `JSON.repair_file(path)` / `JSON.repair_io(io)` are convenience wrappers forwarding both options.
|
|
28
28
|
|
|
29
29
|
`JSON::JSONRepairError` is the only error raised for unrecoverable inputs; it exposes the failure `#position`. If the Repairer ever emits a string stdlib cannot parse (a Repairer bug), the `JSON::ParserError` is wrapped in `JSONRepairError` rather than leaked.
|
|
30
30
|
|
data/lib/json/repair/version.rb
CHANGED
data/lib/json/repairer.rb
CHANGED
|
@@ -434,6 +434,24 @@ module JSON
|
|
|
434
434
|
end
|
|
435
435
|
end
|
|
436
436
|
|
|
437
|
+
# Repair elided empty array slots like [1,,2] or [1,,,,2]. Called both
|
|
438
|
+
# at the start of an array (leading commas) and right after a separator
|
|
439
|
+
# comma: skip every comma found here (each marks a slot with no value
|
|
440
|
+
# between it and the previous one or the opening bracket), so [1,,2] ->
|
|
441
|
+
# [1,2], [1,,,,2] -> [1,2], and [,,1] -> [1]. Dropping the slot — rather
|
|
442
|
+
# than inserting null — extends the single-leading-comma repair ([,1] ->
|
|
443
|
+
# [1]) uniformly. Divergence from upstream, which mangles [1,,2] into
|
|
444
|
+
# [[1],2] via its root comma-sequence wrap and raises on [1,,,,2] (as of
|
|
445
|
+
# v3.14.0).
|
|
446
|
+
def skip_elided_commas
|
|
447
|
+
loop do
|
|
448
|
+
parse_whitespace_and_skip_comments
|
|
449
|
+
break unless @json[@index] == COMMA
|
|
450
|
+
|
|
451
|
+
@index += 1
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
437
455
|
# Parse a string enclosed by double quotes "...". Can contain escaped quotes
|
|
438
456
|
# Repair strings enclosed in single quotes or special quotes
|
|
439
457
|
# Repair an escaped string
|
|
@@ -741,6 +759,24 @@ module JSON
|
|
|
741
759
|
# Parse a number like 2.4 or 2.4e6
|
|
742
760
|
def parse_number
|
|
743
761
|
start = @index
|
|
762
|
+
|
|
763
|
+
# Divergence from upstream: accept and discard a leading "+". JSON5
|
|
764
|
+
# permits an explicit plus; JSON does not, so "+1.23" -> "1.23" and
|
|
765
|
+
# "{"a": +5}" -> {"a":5}. The "+" must be followed by a digit or a
|
|
766
|
+
# leading dot (mirroring the "-" branch below); otherwise this is not
|
|
767
|
+
# a number and we backtrack to the "+" so a bare "+" still raises.
|
|
768
|
+
# `start` stays on the "+", so the reset paths restore @index cleanly
|
|
769
|
+
# and the @index > start exponent guard below still implies a digit
|
|
770
|
+
# was consumed; the two emission sites drop the leading "+" before
|
|
771
|
+
# quoting. Upstream leaves "+1.23" unrepaired.
|
|
772
|
+
if @json[@index] == PLUS
|
|
773
|
+
@index += 1
|
|
774
|
+
unless digit?(@json[@index]) || @json[@index] == DOT
|
|
775
|
+
@index = start
|
|
776
|
+
return false
|
|
777
|
+
end
|
|
778
|
+
end
|
|
779
|
+
|
|
744
780
|
if @json[@index] == '-'
|
|
745
781
|
@index += 1
|
|
746
782
|
if at_end_of_number?
|
|
@@ -802,7 +838,9 @@ module JSON
|
|
|
802
838
|
|
|
803
839
|
if @index > start
|
|
804
840
|
# repair a number with leading zeros like "00789"
|
|
805
|
-
|
|
841
|
+
# drop a leading "+" first (see the PLUS branch above), so "+05"
|
|
842
|
+
# quotes like "05" and "+1.23" emits as "1.23"
|
|
843
|
+
num = @json[start...@index].delete_prefix(PLUS)
|
|
806
844
|
# the optional sign quotes "-05" like "05" (divergence from
|
|
807
845
|
# upstream, whose unsigned check lets "-05" through unrepaired)
|
|
808
846
|
has_invalid_leading_zero = num.match?(/^-?0\d/)
|
|
@@ -825,8 +863,9 @@ module JSON
|
|
|
825
863
|
@index += 1
|
|
826
864
|
parse_whitespace_and_skip_comments
|
|
827
865
|
|
|
828
|
-
# repair: skip leading
|
|
829
|
-
|
|
866
|
+
# repair: skip leading commas (elided empty slots) like [,1,2,3]
|
|
867
|
+
# or [,,1] — the same drop applied after a separator comma below
|
|
868
|
+
skip_elided_commas
|
|
830
869
|
|
|
831
870
|
initial = true
|
|
832
871
|
while @index < @json.length && @json[@index] != CLOSING_BRACKET
|
|
@@ -837,6 +876,8 @@ module JSON
|
|
|
837
876
|
processed_comma = parse_character(COMMA)
|
|
838
877
|
# repair missing comma
|
|
839
878
|
@output = insert_before_last_whitespace(@output, ',') unless processed_comma
|
|
879
|
+
# repair: drop elided empty slots like [1,,2] -> [1,2]
|
|
880
|
+
skip_elided_commas if processed_comma
|
|
840
881
|
end
|
|
841
882
|
|
|
842
883
|
skip_ellipsis
|
|
@@ -908,7 +949,8 @@ module JSON
|
|
|
908
949
|
# repair numbers cut off at the end
|
|
909
950
|
# this will only be called when we end after a '.', '-', or 'e' and does not
|
|
910
951
|
# change the number more than it needs to make it valid JSON
|
|
911
|
-
|
|
952
|
+
# delete_prefix drops a leading "+" (see the PLUS branch in parse_number)
|
|
953
|
+
num = "#{@json[start...@index]}0".delete_prefix(PLUS)
|
|
912
954
|
# quote a padded token that has an invalid leading zero, like "05e" ->
|
|
913
955
|
# "05e0", applying the same rule as the end of parse_number (divergence
|
|
914
956
|
# from upstream, which emits the invalid number raw)
|
data/sig/json/repairer.rbs
CHANGED
|
@@ -66,6 +66,9 @@ module JSON
|
|
|
66
66
|
# or a similar construct in objects.
|
|
67
67
|
def skip_ellipsis: () -> void
|
|
68
68
|
|
|
69
|
+
# Drop elided empty array slots like [1,,2] -> [1,2].
|
|
70
|
+
def skip_elided_commas: () -> void
|
|
71
|
+
|
|
69
72
|
# Parse a string enclosed by double quotes "...". Can contain escaped quotes
|
|
70
73
|
# Repair strings enclosed in single quotes or special quotes
|
|
71
74
|
# Repair an escaped string
|