Fix Broken JSON Instantly with This Advanced Syntax Corrector GUI Tool in Python
When working with APIs, large datasets, or configuration files, it’s common to run into broken or poorly formatted JSON files. Missing quotes, extra commas, or incorrect brackets can easily break your workflow and lead to frustrating debugging sessions.
To help developers, data analysts, and JSON-handling enthusiasts, we’ve built a desktop GUI tool that not only detects but also automatically fixes common JSON syntax errors — all with a clean and intuitive interface using Python’s tkinter.
🛠️ What is the Advanced JSON Syntax Corrector?
This is a Python-based graphical application that scans JSON files for syntax issues and attempts to fix them intelligently. Built using the standard tkinter library, it requires no external dependencies and offers several helpful features:
- File selection and real-time validation
- Error location with visual context
- Automatic syntax correction (quotes, commas, braces, colons, control characters)
- Option to preserve original structure
- Backup creation for safety
- Full error log with helpful context
👁️ User Interface Overview
Upon launch, you’re greeted with a clear interface that guides you through selecting your JSON file. After selection, the tool:
- Analyzes the file for syntax errors using Python’s
jsonmodule. - Displays error messages along with contextual line info.
- Offers buttons to fix errors or view the original file.
- Allows toggling options like “Preserve Structure” and “Create Backup”.
Everything is bundled into a single window, making this tool a perfect utility for quick corrections or batch work.
💡 How Does It Work Behind the Scenes?
The script uses regular expressions (re) to fix the most common issues seen in broken JSON:
- Trailing commas → Removed safely
- Unquoted keys → Wrapped in double quotes
- Single quotes → Converted to standard JSON double quotes
- Unescaped control characters → Escaped for JSON compliance
- Missing colons or incorrect syntax → Heuristically repaired
If the file is too malformed for full recovery, it still gives you contextual insight to edit manually.
🧠 Smart Error Detection Example
If your JSON file contains an error like this:
jsonCopyEdit{
name: 'John',
age: 30,
}
The tool will correct it to:
jsonCopyEdit{
"name": "John",
"age": 30
}
All in one click, without the need to use an online validator or reformat manually.
🔐 Safety First: Optional Backup Feature
Before making any changes, you can choose to create a .bak backup of the original file. This ensures you always have the untouched version, just in case.
🧰 Ideal Use Cases
- Fixing poorly formatted logs or API responses
- Repairing JSON exports from third-party tools
- Cleaning up hand-written JSON
- Teaching students JSON structure with real-time feedback
- Quickly validating and editing config files for apps, games, or services
🖥️ How to Run It
- Make sure you have Python installed (3.6+ recommended).
- Save the script as
json_corrector.py. - Run it:
bashCopyEditpython json_corrector.py
- Use the UI to select a file and fix it!
✅ Final Thoughts
This tool is an excellent addition to any developer’s toolbox — especially if you’re frequently debugging JSON or managing data from unpredictable sources. It saves time, prevents errors, and gives you full control over the correction process.
import tkinter as tk
from tkinter import messagebox, filedialog, scrolledtext
import json
import re
class SyntaxCorrector:
def __init__(self, master):
self.master = master
self.master.title("Advanced JSON Syntax Corrector")
self.master.geometry("700x500")
# UI Elements
self.label = tk.Label(master, text="Select a file to check and fix JSON syntax errors:")
self.label.pack(pady=10)
self.select_button = tk.Button(master, text="Select File", command=self.select_file)
self.select_button.pack(pady=5)
self.file_info = tk.Label(master, text="No file selected", fg="gray")
self.file_info.pack(pady=5)
# Error display area
self.error_frame = tk.LabelFrame(master, text="Errors Found")
self.error_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
self.error_text = scrolledtext.ScrolledText(self.error_frame, height=10, wrap=tk.WORD)
self.error_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
# Fix options frame
self.options_frame = tk.Frame(master)
self.options_frame.pack(fill=tk.X, padx=10, pady=5)
self.preserve_structure_var = tk.BooleanVar(value=True)
self.preserve_check = tk.Checkbutton(self.options_frame, text="Preserve original structure",
variable=self.preserve_structure_var)
self.preserve_check.pack(side=tk.LEFT, padx=5)
self.backup_var = tk.BooleanVar(value=True)
self.backup_check = tk.Checkbutton(self.options_frame, text="Create backup",
variable=self.backup_var)
self.backup_check.pack(side=tk.LEFT, padx=5)
# Buttons
self.button_frame = tk.Frame(master)
self.button_frame.pack(fill=tk.X, padx=10, pady=10)
self.fix_button = tk.Button(self.button_frame, text="Fix Errors", command=self.fix_errors,
state=tk.DISABLED, bg="#4CAF50", fg="white")
self.fix_button.pack(side=tk.LEFT, padx=5)
self.view_button = tk.Button(self.button_frame, text="View Original", command=self.view_original,
state=tk.DISABLED)
self.view_button.pack(side=tk.LEFT, padx=5)
self.status_label = tk.Label(master, text="", fg="blue")
self.status_label.pack(pady=5)
# Properties
self.file_path = None
self.errors = []
self.content = ""
def select_file(self):
self.file_path = filedialog.askopenfilename(filetypes=[("JSON Files", "*.json"), ("All Files", "*.*")])
if not self.file_path:
return
self.status_label.config(text="Checking for errors...")
self.file_info.config(text=f"File: {self.file_path}")
self.master.update_idletasks()
try:
with open(self.file_path, "r", encoding="utf-8") as file:
self.content = file.read()
except UnicodeDecodeError:
try:
with open(self.file_path, "r", encoding="latin-1") as file:
self.content = file.read()
except Exception as e:
messagebox.showerror("Error", f"Failed to open file: {str(e)}")
self.status_label.config(text="Error opening file")
return
self.errors = self.check_json_syntax(self.content)
self.display_errors()
def check_json_syntax(self, content):
errors = []
try:
json.loads(content)
except json.JSONDecodeError as e:
line_col = f"Line {e.lineno}, Column {e.colno}"
error_message = f"{line_col}: {e.msg}"
# Extract the problematic line and highlight the issue
lines = content.split('\n')
if 0 <= e.lineno - 1 < len(lines):
error_line = lines[e.lineno - 1]
context = f"Context: {error_line}\n{' ' * (e.colno - 1)}^ Error might be here"
else:
context = "Could not extract context"
errors.append({
"message": error_message,
"context": context,
"line": e.lineno,
"column": e.colno,
"error": str(e)
})
return errors
def display_errors(self):
self.error_text.delete(1.0, tk.END)
if not self.errors:
self.error_text.insert(tk.END, "No syntax errors found! Your JSON is valid.")
self.status_label.config(text="File is valid JSON")
self.fix_button.config(state=tk.DISABLED)
self.view_button.config(state=tk.NORMAL)
else:
for i, error in enumerate(self.errors, 1):
self.error_text.insert(tk.END, f"Error {i}: {error['message']}\n")
self.error_text.insert(tk.END, f"{error['context']}\n\n")
self.status_label.config(text=f"Found {len(self.errors)} error(s). Click 'Fix Errors' to attempt repair.")
self.fix_button.config(state=tk.NORMAL)
self.view_button.config(state=tk.NORMAL)
def view_original(self):
if not self.file_path:
return
view_window = tk.Toplevel(self.master)
view_window.title(f"Original JSON: {self.file_path}")
view_window.geometry("700x500")
text_area = scrolledtext.ScrolledText(view_window, wrap=tk.WORD)
text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
text_area.insert(tk.END, self.content)
text_area.config(state=tk.DISABLED)
def fix_errors(self):
if not self.file_path:
return
fixed_content = self.fix_json(self.content)
# Create backup if requested
if self.backup_var.get():
import shutil
backup_file = f"{self.file_path}.bak"
try:
shutil.copy2(self.file_path, backup_file)
self.status_label.config(text=f"Backup created: {backup_file}")
except Exception as e:
messagebox.showwarning("Backup Failed", f"Could not create backup: {str(e)}")
# Save fixed file
try:
# Decide where to save the fixed content
save_path = self.file_path
with open(save_path, "w", encoding="utf-8") as file:
file.write(fixed_content)
messagebox.showinfo("Success", f"JSON fixed successfully!\nSaved to: {save_path}")
self.status_label.config(text="JSON fixed successfully!")
# Refresh to show the fixed file is now valid
self.content = fixed_content
self.errors = self.check_json_syntax(self.content)
self.display_errors()
except Exception as e:
messagebox.showerror("Error", f"Failed to save fixed file: {str(e)}")
def fix_json(self, content):
# First, detect if the JSON is supposed to be an object or array
is_object = False
is_array = False
# Get the first non-whitespace character to determine structure
stripped = content.strip()
if stripped:
first_char = stripped[0]
is_object = first_char == '{'
is_array = first_char == '['
try:
# Step 1: Remove trailing commas
content = re.sub(r",\s*([\]}])", r"\1", content)
# Step 2: Fix missing quotes around property names
content = re.sub(r'([{,]\s*)([a-zA-Z0-9_$]+)(\s*:)', r'\1"\2"\3', content)
# Step 3: Replace single quotes with double quotes (but handle escaped quotes properly)
content = re.sub(r"(?<![\\])(')(.*?)(?<![\\])(')", r'"\2"', content)
# Step 4: Handle escaped single quotes
content = content.replace("\\'", "'")
# Step 5: Fix unescaped control characters
for char in ['\n', '\r', '\t']:
content = content.replace(char, f"\\{char}")
# Step 6: Ensure unescaped quotes inside strings are escaped
# This is complex, so we'll do a simplified version
def escape_quotes_in_strings(match):
prefix = match.group(1)
string_content = match.group(2)
# Escape unescaped double quotes
fixed_content = re.sub(r'(?<![\\])"', r'\\"', string_content)
return f'{prefix}"{fixed_content}"'
# This pattern might need more refinement for complex cases
content = re.sub(r'([:,\[\{]\s*)"(.*?)(?<![\\])"', escape_quotes_in_strings, content)
# Step 7: Fix missing colons
content = re.sub(r'("[\w\s]+")(\s+)("[\w\s]+"|\d+|\{|\[|true|false|null)', r'\1: \3', content)
# Try to parse the fixed content
try:
parsed_data = json.loads(content)
# If we successfully parsed, reformat with proper indentation
if self.preserve_structure_var.get() and (is_object or is_array):
# Preserve the original structure (object or array)
return json.dumps(parsed_data, indent=2)
else:
# Use the parsed structure directly
return json.dumps(parsed_data, indent=2)
except json.JSONDecodeError:
# If first fix attempt failed, try more aggressive measures
# Alternative approach: try to wrap with proper structure
if not (stripped.startswith('{') or stripped.startswith('[')):
# If not starting with { or [, try to detect if it's an object without braces
if ':' in stripped and not stripped.startswith('"'):
# Likely an object with missing braces
wrapped_content = '{' + stripped + '}'
else:
# Try as array
wrapped_content = '[' + stripped + ']'
try:
parsed_data = json.loads(wrapped_content)
return json.dumps(parsed_data, indent=2)
except:
pass
# If all else fails, notify user we couldn't fully fix it
messagebox.showwarning("Warning",
"Advanced fixes applied but JSON may still have issues. Manual editing might be required.")
return content
except Exception as e:
messagebox.showerror("Error", f"Failed to fix JSON: {str(e)}")
return content
if __name__ == "__main__":
root = tk.Tk()
app = SyntaxCorrector(root)
root.mainloop()