json-repair 0.47.7__tar.gz → 0.47.8__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.
- {json_repair-0.47.7/src/json_repair.egg-info → json_repair-0.47.8}/PKG-INFO +1 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/pyproject.toml +1 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/object_comparer.py +15 -4
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_array.py +2 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_object.py +6 -11
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_string.py +9 -1
- {json_repair-0.47.7 → json_repair-0.47.8/src/json_repair.egg-info}/PKG-INFO +1 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_json_repair.py +0 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_array.py +2 -1
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_string.py +1 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/LICENSE +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/README.md +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/setup.cfg +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/__init__.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/__main__.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/constants.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/json_context.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/json_parser.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/json_repair.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_boolean_or_null.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_comment.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/parse_number.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/py.typed +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair/string_file_wrapper.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair.egg-info/SOURCES.txt +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair.egg-info/dependency_links.txt +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair.egg-info/entry_points.txt +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/src/json_repair.egg-info/top_level.txt +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_boolean_or_null.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_comment.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_number.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_parse_object.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_performance.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_repair_json_cli.py +0 -0
- {json_repair-0.47.7 → json_repair-0.47.8}/tests/test_repair_json_from_file.py +0 -0
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
4
4
|
[project]
|
5
5
|
name = "json_repair"
|
6
|
-
version = "0.47.
|
6
|
+
version = "0.47.8"
|
7
7
|
license = {file = "LICENSE"}
|
8
8
|
authors = [
|
9
9
|
{ name="Stefano Baccianella", email="4247706+mangiucugna@users.noreply.github.com" },
|
@@ -17,20 +17,31 @@ class ObjectComparer: # pragma: no cover
|
|
17
17
|
return False
|
18
18
|
|
19
19
|
if isinstance(obj1, dict):
|
20
|
-
#
|
21
|
-
if len(obj1) != len(obj2):
|
20
|
+
# Check that both are dicts and same length
|
21
|
+
if not isinstance(obj2, dict) or len(obj1) != len(obj2):
|
22
22
|
return False
|
23
23
|
for key in obj1:
|
24
24
|
if key not in obj2:
|
25
25
|
return False
|
26
|
+
# Recursively compare each value
|
26
27
|
if not ObjectComparer.is_same_object(obj1[key], obj2[key]):
|
27
28
|
return False
|
28
29
|
return True
|
29
30
|
|
30
31
|
elif isinstance(obj1, list):
|
31
|
-
|
32
|
+
# Check that both are lists and same length
|
33
|
+
if not isinstance(obj2, list) or len(obj1) != len(obj2):
|
32
34
|
return False
|
35
|
+
# Recursively compare each item
|
33
36
|
return all(ObjectComparer.is_same_object(obj1[i], obj2[i]) for i in range(len(obj1)))
|
34
37
|
|
35
|
-
# For
|
38
|
+
# For atomic values: types already match, so return True
|
36
39
|
return True
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
def is_strictly_empty(value: Any) -> bool:
|
43
|
+
"""
|
44
|
+
Returns True if value is an empty container (str, list, dict, set, tuple).
|
45
|
+
Returns False for non-containers like None, 0, False, etc.
|
46
|
+
"""
|
47
|
+
return isinstance(value, str | list | dict | set | tuple) and len(value) == 0
|
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING
|
|
2
2
|
|
3
3
|
from .constants import STRING_DELIMITERS, JSONReturnType
|
4
4
|
from .json_context import ContextValues
|
5
|
+
from .object_comparer import ObjectComparer
|
5
6
|
|
6
7
|
if TYPE_CHECKING:
|
7
8
|
from .json_parser import JSONParser
|
@@ -28,7 +29,7 @@ def parse_array(self: "JSONParser") -> list[JSONReturnType]:
|
|
28
29
|
value = self.parse_json()
|
29
30
|
|
30
31
|
# It is possible that parse_json() returns nothing valid, so we increase by 1
|
31
|
-
if value
|
32
|
+
if ObjectComparer.is_strictly_empty(value):
|
32
33
|
self.index += 1
|
33
34
|
elif value == "..." and self.get_char_at(-1) == ".":
|
34
35
|
self.log(
|
@@ -64,17 +64,12 @@ def parse_object(self: "JSONParser") -> dict[str, JSONReturnType]:
|
|
64
64
|
# If the string is empty but there is a object divider, we are done here
|
65
65
|
break
|
66
66
|
if ContextValues.ARRAY in self.context.context and key in obj:
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
"While parsing an object we found a duplicate key, closing the object here and rolling back the index",
|
74
|
-
)
|
75
|
-
self.index = rollback_index - 1
|
76
|
-
# add an opening curly brace to make this work
|
77
|
-
self.json_str = self.json_str[: self.index + 1] + "{" + self.json_str[self.index + 1 :]
|
67
|
+
self.log(
|
68
|
+
"While parsing an object we found a duplicate key, closing the object here and rolling back the index",
|
69
|
+
)
|
70
|
+
self.index = rollback_index - 1
|
71
|
+
# add an opening curly brace to make this work
|
72
|
+
self.json_str = self.json_str[: self.index + 1] + "{" + self.json_str[self.index + 1 :]
|
78
73
|
break
|
79
74
|
|
80
75
|
# Skip filler whitespaces
|
@@ -324,10 +324,18 @@ def parse_string(self: "JSONParser") -> str | bool | None:
|
|
324
324
|
i += 1
|
325
325
|
i = self.skip_to_character(character=rstring_delimiter, idx=i)
|
326
326
|
next_c = self.get_char_at(i)
|
327
|
-
# Ok now I found a delimiter, let's skip whitespaces and see if next we find a }
|
327
|
+
# Ok now I found a delimiter, let's skip whitespaces and see if next we find a } or a ,
|
328
328
|
i += 1
|
329
329
|
i = self.skip_whitespaces_at(idx=i, move_main_index=False)
|
330
330
|
next_c = self.get_char_at(i)
|
331
|
+
if next_c in ["}", ","]:
|
332
|
+
self.log(
|
333
|
+
"While parsing a string, we a misplaced quote that would have closed the string but has a different meaning here, ignoring it",
|
334
|
+
)
|
335
|
+
string_acc += str(char)
|
336
|
+
self.index += 1
|
337
|
+
char = self.get_char_at()
|
338
|
+
continue
|
331
339
|
elif next_c == rstring_delimiter and self.get_char_at(i - 1) != "\\":
|
332
340
|
# Check if self.index:self.index+i is only whitespaces, break if that's the case
|
333
341
|
if all(str(self.get_char_at(j)).isspace() for j in range(1, i) if self.get_char_at(j)):
|
@@ -158,4 +158,3 @@ def test_stream_stable():
|
|
158
158
|
assert repair_json('{"key": "val\\n', stream_stable=True) == '{"key": "val\\n"}'
|
159
159
|
assert repair_json('{"key": "val\\n123,`key2:value2', stream_stable=True) == '{"key": "val\\n123,`key2:value2"}'
|
160
160
|
assert repair_json('{"key": "val\\n123,`key2:value2`"}', stream_stable=True) == '{"key": "val\\n123,`key2:value2`"}'
|
161
|
-
assert repair_json('[{"key": "value", "key', stream_stable=True) == '[{"key": "value"}]'
|
@@ -9,7 +9,7 @@ def test_parse_array():
|
|
9
9
|
|
10
10
|
|
11
11
|
def test_parse_array_edge_cases():
|
12
|
-
assert repair_json("[{]") == "[
|
12
|
+
assert repair_json("[{]") == "[]"
|
13
13
|
assert repair_json("[") == "[]"
|
14
14
|
assert repair_json('["') == "[]"
|
15
15
|
assert repair_json("]") == ""
|
@@ -35,3 +35,4 @@ def test_parse_array_edge_cases():
|
|
35
35
|
)
|
36
36
|
assert repair_json('{"k"e"y": "value"}') == '{"k\\"e\\"y": "value"}'
|
37
37
|
assert repair_json('["key":"value"}]') == '[{"key": "value"}]'
|
38
|
+
assert repair_json('[{"key": "value", "key') == '[{"key": "value"}]'
|
@@ -56,6 +56,7 @@ def test_missing_and_mixed_quotes():
|
|
56
56
|
)
|
57
57
|
assert repair_json('{"key": "v"alu"e"} key:') == '{"key": "v\\"alu\\"e"}'
|
58
58
|
assert repair_json('{"key": "v"alue", "key2": "value2"}') == '{"key": "v\\"alue", "key2": "value2"}'
|
59
|
+
assert repair_json('[{"key": "v"alu,e", "key2": "value2"}]') == '[{"key": "v\\"alu,e", "key2": "value2"}]'
|
59
60
|
|
60
61
|
|
61
62
|
def test_escaping():
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|