|
"""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 |
|
|