diff --git a/package-lock.json b/package-lock.json
index b374df4bc9..463ba9ade9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,7 @@
"json5": "^2.2.3",
"jsonata": "^2.0.3",
"jsonpath-plus": "^9.0.0",
+ "jsonrepair": "^3.13.0",
"jsonwebtoken": "8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^11.1.0",
@@ -12520,6 +12521,15 @@
"node": ">=14.0.0"
}
},
+ "node_modules/jsonrepair": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.0.tgz",
+ "integrity": "sha512-5YRzlAQ7tuzV1nAJu3LvDlrKtBFIALHN2+a+I1MGJCt3ldRDBF/bZuvIPzae8Epot6KBXd0awRZZcuoeAsZ/mw==",
+ "license": "ISC",
+ "bin": {
+ "jsonrepair": "bin/cli.js"
+ }
+ },
"node_modules/jsonwebtoken": {
"version": "8.5.1",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
diff --git a/package.json b/package.json
index 9191ab6f03..d55464b381 100644
--- a/package.json
+++ b/package.json
@@ -144,6 +144,7 @@
"json5": "^2.2.3",
"jsonata": "^2.0.3",
"jsonpath-plus": "^9.0.0",
+ "jsonrepair": "^3.13.0",
"jsonwebtoken": "8.5.1",
"jsqr": "^1.4.0",
"jsrsasign": "^11.1.0",
diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 434c8bb619..eae8eed3e8 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -465,6 +465,7 @@
"JavaScript Minify",
"JSON Beautify",
"JSON Minify",
+ "JSON Repair",
"XML Beautify",
"XML Minify",
"SQL Beautify",
diff --git a/src/core/operations/JSONRepair.mjs b/src/core/operations/JSONRepair.mjs
new file mode 100644
index 0000000000..65eacdf702
--- /dev/null
+++ b/src/core/operations/JSONRepair.mjs
@@ -0,0 +1,48 @@
+/**
+ * @author maojunxyz [maojun@linux.com]
+ * @copyright Crown Copyright 2025
+ * @license Apache-2.0
+ */
+
+import OperationError from "../errors/OperationError.mjs";
+import Operation from "../Operation.mjs";
+
+/**
+ * JSON Repair operation
+ */
+class JSONRepair extends Operation {
+
+ /**
+ * JSONRepair constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "JSON Repair";
+ this.module = "Code";
+ this.description = "Attempts to repair invalid JSON by fixing common issues such as missing quotes around keys, trailing commas, single quotes, missing brackets, and more.
This operation can fix:
• Missing quotes around keys
• Single quotes instead of double quotes
• Trailing commas
• Missing commas
• Missing closing brackets
• Python constants (None, True, False)
• Comments
• JSONP notation
• And many other common JSON formatting issues
Uses the jsonrepair library for comprehensive JSON repair.
Tags: json fix, json repair, json validate, json format";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ async run(input, args) {
+ if (!input) return "";
+
+ try {
+ // Dynamic import of jsonrepair to handle potential module loading issues
+ const { jsonrepair } = await import("jsonrepair");
+ return jsonrepair(input);
+
+ } catch (err) {
+ throw new OperationError("Unable to repair JSON. The input contains errors that cannot be automatically fixed.\n" + err.message);
+ }
+ }
+}
+
+export default JSONRepair;
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index f147e9e7c7..cf150ad28a 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -93,6 +93,7 @@ import "./tests/JA3SFingerprint.mjs";
import "./tests/Jsonata.mjs";
import "./tests/JSONBeautify.mjs";
import "./tests/JSONMinify.mjs";
+import "./tests/JSONRepair.mjs";
import "./tests/JSONtoCSV.mjs";
import "./tests/Jump.mjs";
import "./tests/JWK.mjs";
diff --git a/tests/operations/tests/JSONRepair.mjs b/tests/operations/tests/JSONRepair.mjs
new file mode 100644
index 0000000000..9b88bad00f
--- /dev/null
+++ b/tests/operations/tests/JSONRepair.mjs
@@ -0,0 +1,175 @@
+/**
+ * JSONRepair tests.
+ *
+ * @author maojunxyz [maojun@linux.com]
+ *
+ * @copyright Crown Copyright 2025
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "JSON Repair: empty string",
+ input: "",
+ expectedOutput: "",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: valid JSON unchanged",
+ input: "{\"name\": \"John\", \"age\": 30}",
+ expectedOutput: "{\"name\": \"John\", \"age\": 30}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: missing quotes around keys",
+ input: "{name: \"John\", age: 30}",
+ expectedOutput: "{\"name\": \"John\", \"age\": 30}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: single quotes to double quotes",
+ input: "{'name': 'John', 'age': 30}",
+ expectedOutput: "{\"name\": \"John\", \"age\": 30}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: trailing comma in object",
+ input: "{\"name\": \"John\", \"age\": 30,}",
+ expectedOutput: "{\"name\": \"John\", \"age\": 30}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: trailing comma in array",
+ input: "[1, 2, 3,]",
+ expectedOutput: "[1, 2, 3]",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: Python constants",
+ input: "{\"active\": True, \"data\": None, \"flag\": False}",
+ expectedOutput: "{\"active\": true, \"data\": null, \"flag\": false}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: line comments",
+ input: `{
+ "name": "John", // This is a comment
+ "age": 30
+}`,
+ expectedOutput: `{
+ "name": "John",
+ "age": 30
+}`,
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: block comments",
+ input: `{
+ "name": "John", /* This is a
+ multi-line comment */
+ "age": 30
+}`,
+ expectedOutput: `{
+ "name": "John",
+ "age": 30
+}`,
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: missing comma",
+ input: `{
+ "name": "John"
+ "age": 30,
+ "city": "Boston"
+}`,
+ expectedOutput: `{
+ "name": "John",
+ "age": 30,
+ "city": "Boston"
+}`,
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: complex mixed issues",
+ input: `{
+ name: "John", // Person's name
+ age: 30,
+ active: True,
+ data: None,
+}`,
+ expectedOutput: `{
+ "name": "John",
+ "age": 30,
+ "active": true,
+ "data": null
+}`,
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+ {
+ name: "JSON Repair: JSONP notation",
+ input: "callback({\"name\": \"John\", \"age\": 30})",
+ expectedOutput: "{\"name\": \"John\", \"age\": 30}",
+ recipeConfig: [
+ {
+ op: "JSON Repair",
+ args: [],
+ },
+ ],
+ },
+]);