|
|
import json |
|
|
import re |
|
|
from typing import Any, Dict, Union |
|
|
from .file_utils import f_join |
|
|
|
|
|
|
|
|
def json_load(*file_path, **kwargs): |
|
|
file_path = f_join(file_path) |
|
|
with open(file_path, "r") as fp: |
|
|
return json.load(fp, **kwargs) |
|
|
|
|
|
|
|
|
def json_loads(string, **kwargs): |
|
|
return json.loads(string, **kwargs) |
|
|
|
|
|
|
|
|
def json_dump(data, *file_path, **kwargs): |
|
|
file_path = f_join(file_path) |
|
|
with open(file_path, "w") as fp: |
|
|
json.dump(data, fp, **kwargs) |
|
|
|
|
|
|
|
|
def json_dumps(data, **kwargs): |
|
|
""" |
|
|
Returns: string |
|
|
""" |
|
|
return json.dumps(data, **kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
load_json = json_load |
|
|
loads_json = json_loads |
|
|
dump_json = json_dump |
|
|
dumps_json = json_dumps |
|
|
|
|
|
|
|
|
def extract_char_position(error_message: str) -> int: |
|
|
"""Extract the character position from the JSONDecodeError message. |
|
|
Args: |
|
|
error_message (str): The error message from the JSONDecodeError |
|
|
exception. |
|
|
Returns: |
|
|
int: The character position. |
|
|
""" |
|
|
import re |
|
|
|
|
|
char_pattern = re.compile(r"\(char (\d+)\)") |
|
|
if match := char_pattern.search(error_message): |
|
|
return int(match[1]) |
|
|
else: |
|
|
raise ValueError("Character position not found in the error message.") |
|
|
|
|
|
|
|
|
def add_quotes_to_property_names(json_string: str) -> str: |
|
|
""" |
|
|
Add quotes to property names in a JSON string. |
|
|
Args: |
|
|
json_string (str): The JSON string. |
|
|
Returns: |
|
|
str: The JSON string with quotes added to property names. |
|
|
""" |
|
|
|
|
|
def replace_func(match): |
|
|
return f'"{match.group(1)}":' |
|
|
|
|
|
property_name_pattern = re.compile(r"(\w+):") |
|
|
corrected_json_string = property_name_pattern.sub(replace_func, json_string) |
|
|
|
|
|
try: |
|
|
json.loads(corrected_json_string) |
|
|
return corrected_json_string |
|
|
except json.JSONDecodeError as e: |
|
|
raise e |
|
|
|
|
|
|
|
|
def balance_braces(json_string: str) -> str: |
|
|
""" |
|
|
Balance the braces in a JSON string. |
|
|
Args: |
|
|
json_string (str): The JSON string. |
|
|
Returns: |
|
|
str: The JSON string with braces balanced. |
|
|
""" |
|
|
|
|
|
open_braces_count = json_string.count("{") |
|
|
close_braces_count = json_string.count("}") |
|
|
|
|
|
while open_braces_count > close_braces_count: |
|
|
json_string += "}" |
|
|
close_braces_count += 1 |
|
|
|
|
|
while close_braces_count > open_braces_count: |
|
|
json_string = json_string.rstrip("}") |
|
|
close_braces_count -= 1 |
|
|
|
|
|
try: |
|
|
json.loads(json_string) |
|
|
return json_string |
|
|
except json.JSONDecodeError as e: |
|
|
raise e |
|
|
|
|
|
|
|
|
def fix_invalid_escape(json_str: str, error_message: str) -> str: |
|
|
while error_message.startswith("Invalid \\escape"): |
|
|
bad_escape_location = extract_char_position(error_message) |
|
|
json_str = json_str[:bad_escape_location] + json_str[bad_escape_location + 1 :] |
|
|
try: |
|
|
json.loads(json_str) |
|
|
return json_str |
|
|
except json.JSONDecodeError as e: |
|
|
error_message = str(e) |
|
|
return json_str |
|
|
|
|
|
|
|
|
def correct_json(json_str: str) -> str: |
|
|
""" |
|
|
Correct common JSON errors. |
|
|
Args: |
|
|
json_str (str): The JSON string. |
|
|
""" |
|
|
|
|
|
try: |
|
|
json.loads(json_str) |
|
|
return json_str |
|
|
except json.JSONDecodeError as e: |
|
|
error_message = str(e) |
|
|
if error_message.startswith("Invalid \\escape"): |
|
|
json_str = fix_invalid_escape(json_str, error_message) |
|
|
if error_message.startswith( |
|
|
"Expecting property name enclosed in double quotes" |
|
|
): |
|
|
json_str = add_quotes_to_property_names(json_str) |
|
|
try: |
|
|
json.loads(json_str) |
|
|
return json_str |
|
|
except json.JSONDecodeError as e: |
|
|
error_message = str(e) |
|
|
if balanced_str := balance_braces(json_str): |
|
|
return balanced_str |
|
|
return json_str |
|
|
|
|
|
|
|
|
def fix_and_parse_json( |
|
|
json_str: str, try_to_fix_with_gpt: bool = True |
|
|
) -> Union[str, Dict[Any, Any]]: |
|
|
"""Fix and parse JSON string""" |
|
|
try: |
|
|
json_str = json_str.replace("\t", "") |
|
|
return json.loads(json_str) |
|
|
except json.JSONDecodeError as _: |
|
|
json_str = correct_json(json_str) |
|
|
try: |
|
|
return json.loads(json_str) |
|
|
except json.JSONDecodeError as _: |
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
brace_index = json_str.index("{") |
|
|
json_str = json_str[brace_index:] |
|
|
last_brace_index = json_str.rindex("}") |
|
|
json_str = json_str[: last_brace_index + 1] |
|
|
return json.loads(json_str) |
|
|
except json.JSONDecodeError as e: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
raise e |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|