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 (C11) port of the json-repair tool: the two
* scanner passes (quote conversion + truncation recovery) of the 7-pass
* pipeline in typescript.ts / go.go; the regex-shaped passes live in the
* higher-level ports. No JSON engine here — repair returns the fixed text. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct { char *s; size_t n, cap; } Buf; /* growable output buffer */
static void put(Buf *b, char c) {
if (b->n == b->cap) { b->cap = b->cap ? b->cap * 2 : 64; b->s = realloc(b->s, b->cap); }
b->s[b->n++] = c;
}
static void putn(Buf *b, const char *s, size_t k) { while (k--) put(b, *s++); }
static char *fin(Buf *b) { put(b, '\0'); return b->s; }
static void copy_dquote(Buf *b, const char *s, size_t *i) { /* "..." verbatim */
put(b, s[(*i)++]);
while (s[*i] && s[*i] != '"') {
if (s[*i] == '\\' && s[*i + 1]) put(b, s[(*i)++]);
put(b, s[(*i)++]);
}
if (s[*i]) put(b, s[(*i)++]);
}
/* Single-quoted strings -> double-quoted: escape inner double quotes,
* collapse \' to ', keep every other escape; double-quoted copy verbatim. */
static char *single_to_double_quotes(const char *s) {
Buf b = {0};
size_t i = 0;
while (s[i]) {
if (s[i] == '"') copy_dquote(&b, s, &i);
else if (s[i] == '\'') {
put(&b, '"'); i++;
while (s[i] && s[i] != '\'') {
if (s[i] == '\\' && s[i + 1]) {
if (s[i + 1] == '\'') { put(&b, '\''); i += 2; }
else { put(&b, s[i]); put(&b, s[i + 1]); i += 2; }
} else if (s[i] == '"') { putn(&b, "\\\"", 2); i++; }
else { put(&b, s[i]); i++; }
}
if (s[i]) i++; /* skip the closing quote */
put(&b, '"');
} else { put(&b, s[i]); i++; }
}
return fin(&b);
}
/* Recover truncated JSON: close an unterminated string, drop a dangling
* comma, give a dangling colon a null value, then close open brackets. */
static char *close_truncated(const char *s) {
size_t len = strlen(s);
char *stack = malloc(len + 1);
size_t top = 0, i = 0;
int in_str = 0;
while (s[i]) {
char c = s[i];
if (in_str) {
if (c == '\\' && s[i + 1]) i++; /* skip the escaped character */
else if (c == '"') in_str = 0;
} else if (c == '"') in_str = 1;
else if (c == '{' || c == '[') stack[top++] = c;
else if (c == '}' || c == ']') top = top ? top - 1 : 0;
i++;
}
Buf b = {0};
putn(&b, s, i);
if (in_str) put(&b, '"');
while (b.n && (b.s[b.n - 1] == ',' || strchr(" \t\r\n", b.s[b.n - 1]))) b.n--;
if (b.n && b.s[b.n - 1] == ':') putn(&b, " null", 5);
while (top) put(&b, stack[--top] == '{' ? '}' : ']');
free(stack);
return fin(&b);
}
int main(void) {
/* {"name": "Ada", tags: ["x", "y"]} */
char *a = single_to_double_quotes("{'name': 'Ada', tags: ['x', 'y',");
char *r = close_truncated(a);
printf("%s\n", r);
free(a); free(r);
return 0;
}
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 →