JSON Repair — C# source
Fix broken JSON - trailing commas, single quotes, unquoted keys, comments, Python constants, BOM and truncated documents - and get clean pretty-printed JSON plus a list of every repair applied. Runs entirely in your browser.
This is the C# implementation — the same logic the interactive tool runs, in a shareable, citable form.
// JSON repair pipeline — C# (.NET 8, top-level statements) port of the
// json-repair tool: excerpt of the 7-pass pipeline in typescript.ts / go.go
// (quote, Python-constant, trailing comma, truncation repair passes).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
// Single-quoted strings -> double-quoted: escape inner double quotes,
// collapse \' to ', keep every other escape; double-quoted copy verbatim.
string SingleToDoubleQuotes(string text)
{
var sb = new StringBuilder();
int i = 0;
while (i < text.Length)
{
if (text[i] != '\'') { sb.Append(text[i]); i++; continue; }
sb.Append('"');
i++;
while (i < text.Length && text[i] != '\'')
{
if (text[i] == '\\' && i + 1 < text.Length)
{
sb.Append(text[i + 1] == '\'' ? '\'' : $"{text[i]}{text[i + 1]}");
i += 2;
}
else if (text[i] == '"') { sb.Append("\\\""); i++; }
else { sb.Append(text[i]); i++; }
}
sb.Append('"');
i++;
}
return sb.ToString();
}
// Map the segments outside double-quoted strings: Regex.Split keeps the
// captured quoted tokens as their own segments.
string OutsideStrings(string text, Func<string, string> map) =>
string.Join("", Regex.Split(text, "(\"(?:[^\"\\\\]|\\\\.)*\")")
.Select(s => s.StartsWith('"') ? s : map(s)));
string FixPythonConstants(string text) => OutsideStrings(text, s => Regex.Replace(
s, @"\b(True|False|None)\b",
m => m.Value == "True" ? "true" : m.Value == "False" ? "false" : "null"));
// Commas followed only by whitespace and a closing } or ] — dropped.
string StripTrailingCommas(string text) =>
OutsideStrings(text, s => Regex.Replace(s, @",(\s*[}\]])", "$1"));
// Recover truncated JSON: close an unterminated string, drop a dangling
// comma, give a dangling colon a null value, then close open brackets.
string CloseTruncated(string text)
{
var stack = new List<char>();
bool inString = false;
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (inString)
{
if (c == '\\') i++; // skip the escaped character
else if (c == '"') inString = false;
}
else if (c == '"') inString = true;
else if (c == '{' || c == '[') stack.Add(c);
else if (c == '}' || c == ']') { if (stack.Count > 0) stack.RemoveAt(stack.Count - 1); }
}
var out_ = (inString ? text + "\"" : text).TrimEnd(' ', '\t', '\r', '\n', ',');
if (out_.EndsWith(':')) out_ += " null";
stack.Reverse();
return out_ + string.Join("", stack.Select(c => c == '{' ? '}' : ']'));
}
// The canonical repair() also reports which passes fired and pretty-prints.
string Repair(string text) =>
new Func<string, string>[] { SingleToDoubleQuotes, FixPythonConstants,
StripTrailingCommas, CloseTruncated }
.Aggregate(text, (work, pass) => pass(work));
// {"name": "Ada", admin: true, tags: ["x", "y"]}
Console.WriteLine(Repair("{'name': 'Ada', admin: True, tags: ['x', 'y',],"));
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →