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 →