JSON Repair — Ruby 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 Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.
# JSON repair pipeline — Ruby 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).
module JsonRepair
module_function
# Single-quoted strings -> double-quoted: escape inner double quotes,
# collapse \' to ', keep every other escape; double-quoted copy verbatim.
def single_to_double_quotes(text)
out = +''
i = 0
while i < text.length
if text[i] == "'"
out << '"'
i += 1
while i < text.length && text[i] != "'"
if text[i] == '\\' && i + 1 < text.length
out << (text[i + 1] == "'" ? "'" : text[i, 2])
i += 2
elsif text[i] == '"'
out << '\\"'
i += 1
else
out << text[i]
i += 1
end
end
out << '"'
i += 1
else
out << text[i]
i += 1
end
end
out
end
# Map the segments outside double-quoted strings (capture-group split).
def outside_strings(text)
text.split(/("(?:[^"\\]|\\.)*")/).map { |s| s.start_with?('"') ? s : yield(s) }.join
end
def fix_python_constants(text)
outside_strings(text) { |s| s.gsub(/\b(True|False|None)\b/, 'True' => 'true', 'False' => 'false', 'None' => 'null') }
end
# Commas followed only by whitespace and a closing } or ] — dropped.
def strip_trailing_commas(text)
outside_strings(text) { |s| s.gsub(/,(\s*[}\]])/, '\1') }
end
# Recover truncated JSON: close an unterminated string, drop a dangling
# comma, give a dangling colon a null value, then close open brackets.
def close_truncated(text)
stack = []
in_str = false
i = 0
while i < text.length
c = text[i]
if in_str
i += 1 if c == '\\'
in_str = false if c == '"'
elsif c == '"' then in_str = true
elsif c == '{' || c == '[' then stack << c
elsif c == '}' || c == ']' then stack.pop
end
i += 1
end
out = (in_str ? text + '"' : text).sub(/[\s,]+$/, '')
out += ' null' if out.end_with?(':')
out + stack.reverse.map { |c| c == '{' ? '}' : ']' }.join
end
# The canonical repair() also reports which passes fired and pretty-prints.
def repair(text)
%i[single_to_double_quotes fix_python_constants strip_trailing_commas close_truncated]
.reduce(text.to_s) { |work, pass| send(pass, work) }
end
end
# {"name": "Ada", admin: true, tags: ["x", "y"]}
puts 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 →