Skip to content

JSON Repair — Kotlin 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 Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// JSON repair pipeline — Kotlin port of the json-repair tool (excerpt of the
// 7-pass pipeline in typescript.ts / go.go: quote, Python-constant, trailing
// comma, and truncation repair passes).
object JsonRepair {
    private val STRINGS = Regex("(\"(?:[^\"\\\\]|\\\\.)*\")")
    private val PY = Regex("\\b(True|False|None)\\b")
    private val PY_MAP = mapOf("True" to "true", "False" to "false", "None" to "null")
    private val TRAILING = Regex(",(\\s*[}\\]])")
    // Single-quoted -> double-quoted: escape inner double quotes, collapse
    // \' to ', keep other escapes; double-quoted copy verbatim.
    fun singleToDoubleQuotes(text: String): String {
        val out = StringBuilder()
        var i = 0
        while (i < text.length) {
            if (text[i] == '\'') {
                out.append('"'); i++
                while (i < text.length && text[i] != '\'') {
                    when {
                        text[i] == '\\' && i + 1 < text.length -> {
                            out.append(if (text[i + 1] == '\'') '\'' else "${text[i]}${text[i + 1]}")
                            i += 2
                        }
                        text[i] == '"' -> out.append("\\\"").also { i++ }
                        else -> out.append(text[i]).also { i++ }
                    }
                }
                out.append('"')
                i++
            } else {
                out.append(text[i])
                i++
            }
        }
        return out.toString()
    }
    // Transform only the segments outside double-quoted strings: Kotlin's
    // split(Regex) drops delimiters (unlike Ruby/Java), so map only the gaps.
    private fun outsideStrings(text: String, map: (String) -> String): String {
        val out = StringBuilder()
        var last = 0
        for (m in STRINGS.findAll(text)) {
            out.append(map(text.substring(last, m.range.first))).append(m.value)
            last = m.range.last + 1
        }
        return out.append(map(text.substring(last))).toString()
    }
    fun fixPythonConstants(text: String) =
        outsideStrings(text) { PY.replace(it) { m -> PY_MAP.getValue(m.value) } }
    // Commas followed only by whitespace and a closing } or ] — dropped.
    fun stripTrailingCommas(text: String) =
        outsideStrings(text) { TRAILING.replace(it) { it.groupValues[1] } }
    // Recover truncated JSON: close an unterminated string, drop a dangling
    // comma, give a colon null, close open brackets.
    fun closeTruncated(text: String): String {
        val stack = ArrayDeque<Char>()
        var inStr = false
        var i = 0
        while (i < text.length) {
            val c = text[i]
            when {
                inStr -> { if (c == '\\') i++; if (c == '"') inStr = false }
                c == '"' -> inStr = true
                c == '{' || c == '[' -> stack.addLast(c)
                c == '}' || c == ']' -> if (stack.isNotEmpty()) stack.removeLast()
            }
            i++
        }
        var out = (if (inStr) "$text\"" else text).replace(Regex("[\\s,]+$"), "")
        if (out.endsWith(":")) out += " null"
        return out + stack.reversed().joinToString("") { if (it == '{') "}" else "]" }
    }
    // The canonical repair() also reports which passes fired and pretty-prints.
    fun repair(text: String) =
        listOf(::singleToDoubleQuotes, ::fixPythonConstants, ::stripTrailingCommas, ::closeTruncated)
            .fold(text) { work, pass -> pass(work) }
}
// {"name": "Ada", admin: true, tags: ["x", "y"]}
fun main() {
    println(JsonRepair.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 →