Skip to content

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 →