Skip to content

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 →