json-repair 0.28.0__py3-none-any.whl → 0.28.1__py3-none-any.whl
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/json_repair.py +103 -10
- {json_repair-0.28.0.dist-info → json_repair-0.28.1.dist-info}/METADATA +1 -1
- json_repair-0.28.1.dist-info/RECORD +8 -0
- json_repair-0.28.0.dist-info/RECORD +0 -8
- {json_repair-0.28.0.dist-info → json_repair-0.28.1.dist-info}/LICENSE +0 -0
- {json_repair-0.28.0.dist-info → json_repair-0.28.1.dist-info}/WHEEL +0 -0
- {json_repair-0.28.0.dist-info → json_repair-0.28.1.dist-info}/top_level.txt +0 -0
json_repair/json_repair.py
CHANGED
@@ -24,7 +24,7 @@ All supported use cases are in the unit tests
|
|
24
24
|
|
25
25
|
import os
|
26
26
|
import json
|
27
|
-
from typing import Any, Dict, List, Optional, Union, TextIO, Tuple
|
27
|
+
from typing import Any, Dict, List, Optional, Union, TextIO, Tuple, overload, Literal
|
28
28
|
|
29
29
|
|
30
30
|
class StringFileWrapper:
|
@@ -177,7 +177,7 @@ class JSONParser:
|
|
177
177
|
# <member> starts with a <string>
|
178
178
|
key = ""
|
179
179
|
while self.get_char_at():
|
180
|
-
key = self.parse_string()
|
180
|
+
key = str(self.parse_string())
|
181
181
|
|
182
182
|
if key != "" or (key == "" and self.get_char_at() == ":"):
|
183
183
|
# If the string is empty but there is a object divider, we are done here
|
@@ -255,7 +255,7 @@ class JSONParser:
|
|
255
255
|
self.reset_context()
|
256
256
|
return arr
|
257
257
|
|
258
|
-
def parse_string(self) -> Union[str,
|
258
|
+
def parse_string(self) -> Union[str, bool, None]:
|
259
259
|
# <string> is a string of valid characters enclosed in quotes
|
260
260
|
# i.e. { name: "John" }
|
261
261
|
# Somehow all weird cases in an invalid JSON happen to be resolved in this function, so be careful here
|
@@ -382,7 +382,7 @@ class JSONParser:
|
|
382
382
|
string_acc += char
|
383
383
|
self.index += 1
|
384
384
|
char = self.get_char_at()
|
385
|
-
if len(string_acc) > 0 and string_acc[-1] == "\\":
|
385
|
+
if char and len(string_acc) > 0 and string_acc[-1] == "\\":
|
386
386
|
# This is a special case, if people use real strings this might happen
|
387
387
|
self.log("Found a stray escape sequence, normalizing it", "info")
|
388
388
|
string_acc = string_acc[:-1]
|
@@ -473,7 +473,7 @@ class JSONParser:
|
|
473
473
|
"While parsing a string, we a misplaced quote that would have closed the string but has a different meaning here since this is the last element of the object, ignoring it",
|
474
474
|
"info",
|
475
475
|
)
|
476
|
-
string_acc += char
|
476
|
+
string_acc += str(char)
|
477
477
|
self.index += 1
|
478
478
|
char = self.get_char_at()
|
479
479
|
elif next_c == rstring_delimiter:
|
@@ -503,7 +503,7 @@ class JSONParser:
|
|
503
503
|
"While parsing a string, we a misplaced quote that would have closed the string but has a different meaning here, ignoring it",
|
504
504
|
"info",
|
505
505
|
)
|
506
|
-
string_acc += char
|
506
|
+
string_acc += str(char)
|
507
507
|
self.index += 1
|
508
508
|
char = self.get_char_at()
|
509
509
|
|
@@ -521,7 +521,8 @@ class JSONParser:
|
|
521
521
|
if self.get_char_at() not in [":", ","]:
|
522
522
|
return ""
|
523
523
|
|
524
|
-
# A fallout of the previous special case in the while loop,
|
524
|
+
# A fallout of the previous special case in the while loop,
|
525
|
+
# we need to update the index only if we had a closing quote
|
525
526
|
if char != rstring_delimiter:
|
526
527
|
self.log(
|
527
528
|
"While parsing a string, we missed the closing quote, ignoring",
|
@@ -563,6 +564,7 @@ class JSONParser:
|
|
563
564
|
# <boolean> is one of the literal strings 'true', 'false', or 'null' (unquoted)
|
564
565
|
starting_index = self.index
|
565
566
|
char = (self.get_char_at() or "").lower()
|
567
|
+
value = None
|
566
568
|
if char == "t":
|
567
569
|
value = ("true", True)
|
568
570
|
elif char == "f":
|
@@ -583,7 +585,7 @@ class JSONParser:
|
|
583
585
|
self.index = starting_index
|
584
586
|
return ""
|
585
587
|
|
586
|
-
def get_char_at(self, count: int = 0) -> Union[str,
|
588
|
+
def get_char_at(self, count: int = 0) -> Union[str, Literal[False]]:
|
587
589
|
# Why not use something simpler? Because try/except in python is a faster alternative to an "if" statement that is often True
|
588
590
|
try:
|
589
591
|
return self.json_str[self.index + count]
|
@@ -630,6 +632,50 @@ class JSONParser:
|
|
630
632
|
)
|
631
633
|
|
632
634
|
|
635
|
+
@overload
|
636
|
+
def repair_json(
|
637
|
+
json_str: str = "",
|
638
|
+
return_objects: Optional[Literal[False]] = False,
|
639
|
+
skip_json_loads: Optional[bool] = False,
|
640
|
+
logging: Optional[Literal[False]] = False, # None is treated as False
|
641
|
+
json_fd: Optional[TextIO] = None,
|
642
|
+
ensure_ascii: Optional[bool] = True,
|
643
|
+
) -> str: ...
|
644
|
+
|
645
|
+
|
646
|
+
@overload
|
647
|
+
def repair_json(
|
648
|
+
json_str: str = "",
|
649
|
+
return_objects: Literal[True] = True,
|
650
|
+
skip_json_loads: Optional[bool] = False,
|
651
|
+
logging: Optional[Literal[False]] = False, # None is treated as False
|
652
|
+
json_fd: Optional[TextIO] = None,
|
653
|
+
ensure_ascii: Optional[bool] = True,
|
654
|
+
) -> JSONReturnType: ...
|
655
|
+
|
656
|
+
|
657
|
+
@overload
|
658
|
+
def repair_json(
|
659
|
+
json_str: str = "",
|
660
|
+
return_objects: Optional[Literal[False]] = False, # None is treated as False
|
661
|
+
skip_json_loads: Optional[bool] = False,
|
662
|
+
logging: Literal[True] = True,
|
663
|
+
json_fd: Optional[TextIO] = None,
|
664
|
+
ensure_ascii: Optional[bool] = True,
|
665
|
+
) -> Tuple[str, List[Dict[str, str]]]: ...
|
666
|
+
|
667
|
+
|
668
|
+
@overload
|
669
|
+
def repair_json(
|
670
|
+
json_str: str = "",
|
671
|
+
return_objects: Literal[True] = True,
|
672
|
+
skip_json_loads: Optional[bool] = False,
|
673
|
+
logging: Literal[True] = True,
|
674
|
+
json_fd: Optional[TextIO] = None,
|
675
|
+
ensure_ascii: Optional[bool] = True,
|
676
|
+
) -> Tuple[JSONReturnType, List[Dict[str, str]]]: ...
|
677
|
+
|
678
|
+
|
633
679
|
def repair_json(
|
634
680
|
json_str: str = "",
|
635
681
|
return_objects: Optional[bool] = False,
|
@@ -643,7 +689,7 @@ def repair_json(
|
|
643
689
|
It will return the fixed string by default.
|
644
690
|
When `return_objects=True` is passed, it will return the decoded data structure instead.
|
645
691
|
When `skip_json_loads=True` is passed, it will not call the built-in json.loads() function
|
646
|
-
When `logging=True` is passed, it will return
|
692
|
+
When `logging=True` is passed, it will return a tuple with the repaired json and a log of all repair actions
|
647
693
|
"""
|
648
694
|
parser = JSONParser(json_str, json_fd, logging)
|
649
695
|
if skip_json_loads:
|
@@ -656,12 +702,29 @@ def repair_json(
|
|
656
702
|
parsed_json = json.loads(json_str)
|
657
703
|
except json.JSONDecodeError:
|
658
704
|
parsed_json = parser.parse()
|
659
|
-
# It's useful to return the actual object instead of the json string,
|
705
|
+
# It's useful to return the actual object instead of the json string,
|
706
|
+
# it allows this lib to be a replacement of the json library
|
660
707
|
if return_objects or logging:
|
661
708
|
return parsed_json
|
662
709
|
return json.dumps(parsed_json, ensure_ascii=ensure_ascii)
|
663
710
|
|
664
711
|
|
712
|
+
@overload
|
713
|
+
def loads(
|
714
|
+
json_str: str,
|
715
|
+
skip_json_loads: Optional[bool] = False,
|
716
|
+
logging: Optional[Literal[False]] = False, # None is treated as False
|
717
|
+
) -> JSONReturnType: ...
|
718
|
+
|
719
|
+
|
720
|
+
@overload
|
721
|
+
def loads(
|
722
|
+
json_str: str,
|
723
|
+
skip_json_loads: Optional[bool] = False,
|
724
|
+
logging: Literal[True] = True,
|
725
|
+
) -> Tuple[JSONReturnType, List[Dict[str, str]]]: ...
|
726
|
+
|
727
|
+
|
665
728
|
def loads(
|
666
729
|
json_str: str,
|
667
730
|
skip_json_loads: Optional[bool] = False,
|
@@ -679,6 +742,20 @@ def loads(
|
|
679
742
|
)
|
680
743
|
|
681
744
|
|
745
|
+
@overload
|
746
|
+
def load(
|
747
|
+
fd: TextIO,
|
748
|
+
skip_json_loads: Optional[bool] = False,
|
749
|
+
logging: Optional[Literal[False]] = False,
|
750
|
+
) -> JSONReturnType: ...
|
751
|
+
|
752
|
+
|
753
|
+
@overload
|
754
|
+
def load(
|
755
|
+
fd: TextIO, skip_json_loads: Optional[bool] = False, logging: Literal[True] = True
|
756
|
+
) -> Tuple[JSONReturnType, List[Dict[str, str]]]: ...
|
757
|
+
|
758
|
+
|
682
759
|
def load(
|
683
760
|
fd: TextIO, skip_json_loads: Optional[bool] = False, logging: Optional[bool] = False
|
684
761
|
) -> Union[JSONReturnType, Tuple[JSONReturnType, List[Dict[str, str]]]]:
|
@@ -694,6 +771,22 @@ def load(
|
|
694
771
|
)
|
695
772
|
|
696
773
|
|
774
|
+
@overload
|
775
|
+
def from_file(
|
776
|
+
filename: str,
|
777
|
+
skip_json_loads: Optional[bool] = False,
|
778
|
+
logging: Optional[Literal[False]] = False,
|
779
|
+
) -> JSONReturnType: ...
|
780
|
+
|
781
|
+
|
782
|
+
@overload
|
783
|
+
def from_file(
|
784
|
+
filename: str,
|
785
|
+
skip_json_loads: Optional[bool] = False,
|
786
|
+
logging: Literal[True] = True,
|
787
|
+
) -> Tuple[JSONReturnType, List[Dict[str, str]]]: ...
|
788
|
+
|
789
|
+
|
697
790
|
def from_file(
|
698
791
|
filename: str,
|
699
792
|
skip_json_loads: Optional[bool] = False,
|
@@ -0,0 +1,8 @@
|
|
1
|
+
json_repair/__init__.py,sha256=IIzSm1DsCRrr8seF3UeMZXwxcq-tE3j-8d1WBxvEJvE,178
|
2
|
+
json_repair/json_repair.py,sha256=_NBAaY6iqIp1cB1W-lnQ3uS6nc5DjZZyf_HeklYmDyY,32502
|
3
|
+
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
json_repair-0.28.1.dist-info/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
5
|
+
json_repair-0.28.1.dist-info/METADATA,sha256=i9SaIoWFc7YjuQLLN1Rd8GsmVAy0rWJ9-fNwsVDR_KA,8043
|
6
|
+
json_repair-0.28.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
7
|
+
json_repair-0.28.1.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
8
|
+
json_repair-0.28.1.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
json_repair/__init__.py,sha256=IIzSm1DsCRrr8seF3UeMZXwxcq-tE3j-8d1WBxvEJvE,178
|
2
|
-
json_repair/json_repair.py,sha256=5WjVoNO7Grdq9wfnbSjggiVlDv2XVcvWbCIQVmREP38,30109
|
3
|
-
json_repair/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
json_repair-0.28.0.dist-info/LICENSE,sha256=wrjQo8MhNrNCicXtMe3MHmS-fx8AmQk1ue8AQwiiFV8,1076
|
5
|
-
json_repair-0.28.0.dist-info/METADATA,sha256=rPrWno-My7ZQt7EJIR0BodmpLQ84fr0vjJwBM4XYmYU,8043
|
6
|
-
json_repair-0.28.0.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
7
|
-
json_repair-0.28.0.dist-info/top_level.txt,sha256=7-VZwZN2CgB_n0NlSLk-rEUFh8ug21lESbsblOYuZqw,12
|
8
|
-
json_repair-0.28.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|