| """This module contains functions to fix JSON strings using general programmatic approaches, suitable for addressing |
| common JSON formatting issues.""" |
| from __future__ import annotations |
|
|
| import contextlib |
| import json |
| import re |
| from typing import Optional |
|
|
| from autogpt.config import Config |
| from autogpt.json_utils.utilities import extract_char_position |
|
|
| CFG = Config() |
|
|
|
|
| def fix_invalid_escape(json_to_load: str, error_message: str) -> str: |
| """Fix invalid escape sequences in JSON strings. |
| |
| Args: |
| json_to_load (str): The JSON string. |
| error_message (str): The error message from the JSONDecodeError |
| exception. |
| |
| Returns: |
| str: The JSON string with invalid escape sequences fixed. |
| """ |
| while error_message.startswith("Invalid \\escape"): |
| bad_escape_location = extract_char_position(error_message) |
| json_to_load = ( |
| json_to_load[:bad_escape_location] + json_to_load[bad_escape_location + 1 :] |
| ) |
| try: |
| json.loads(json_to_load) |
| return json_to_load |
| except json.JSONDecodeError as e: |
| if CFG.debug_mode: |
| print("json loads error - fix invalid escape", e) |
| error_message = str(e) |
| return json_to_load |
|
|
|
|
| def balance_braces(json_string: str) -> Optional[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 |
|
|
| with contextlib.suppress(json.JSONDecodeError): |
| json.loads(json_string) |
| return json_string |
|
|
|
|
| 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: re.Match) -> str: |
| return f'"{match[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 correct_json(json_to_load: str) -> str: |
| """ |
| Correct common JSON errors. |
| Args: |
| json_to_load (str): The JSON string. |
| """ |
|
|
| try: |
| if CFG.debug_mode: |
| print("json", json_to_load) |
| json.loads(json_to_load) |
| return json_to_load |
| except json.JSONDecodeError as e: |
| if CFG.debug_mode: |
| print("json loads error", e) |
| error_message = str(e) |
| if error_message.startswith("Invalid \\escape"): |
| json_to_load = fix_invalid_escape(json_to_load, error_message) |
| if error_message.startswith( |
| "Expecting property name enclosed in double quotes" |
| ): |
| json_to_load = add_quotes_to_property_names(json_to_load) |
| try: |
| json.loads(json_to_load) |
| return json_to_load |
| except json.JSONDecodeError as e: |
| if CFG.debug_mode: |
| print("json loads error - add quotes", e) |
| error_message = str(e) |
| if balanced_str := balance_braces(json_to_load): |
| return balanced_str |
| return json_to_load |
|
|