Skip to content

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 →