Skip to content

JSON Minify — C source

Remove all whitespace from JSON for the most compact serialization. Validates as it minifies, runs entirely in your browser, with a shareable link to your exact input.

This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.

/* json-minify — compact JSON to its smallest valid form. C99 port (canonical
 * TS: src/lib/json-minify.ts; Go twin: cli/json-minify). No stdlib JSON
 * codec, so — like the Rust port — this is a hand-rolled RFC 8259 scanner:
 * validate, copy tokens verbatim, drop only whitespace; invalid JSON -> 0. */
#include <stdio.h>
#include <string.h>
typedef struct { const char *in; char *out; } sc; /* read cursor / write head */
static void skip_ws(sc *p) { while (*p->in==' '||*p->in=='\t'||*p->in=='\n'||*p->in=='\r') p->in++; }
static void put(sc *p, char c) { *p->out++ = c; }
static int digits(sc *p) { int n = 0; while (*p->in >= '0' && *p->in <= '9') { put(p, *p->in++); n++; } return n; }
/* Strings copy verbatim while escapes are validated (\u takes 4 hex digits);
 * control chars are rejected — NUL counts, so EOF cannot leak into the output. */
static int str_(sc *p) {
    put(p, *p->in++); /* opening quote */
    for (;;) {
        char c = *p->in++;
        if (c == '"') { put(p, c); return 1; }
        if (c == '\\') {
            char e = *p->in++;
            put(p, e);
            if (e == 'u') { for (int k = 0; k < 4; k++) { char h = *p->in++; if (h=='\0'||!strchr("0123456789abcdefABCDEF",h)) return 0; put(p,h); } }
            else if (e == '\0' || !strchr("\"\\/bfnrt", e)) return 0;
        } else if ((unsigned char)c < 0x20) return 0; /* control char / EOF */
        else put(p, c);
    }
}
/* Numbers are grammar-checked and copied verbatim — no float round-trip. */
static int number(sc *p) {
    if (*p->in == '-') put(p, *p->in++);
    if (*p->in == '0') put(p, *p->in++); /* "01" is rejected by the caller */
    else if (*p->in >= '1' && *p->in <= '9') { put(p, *p->in++); digits(p); }
    else return 0;
    if (*p->in == '.') { put(p, *p->in++); if (!digits(p)) return 0; }
    if (*p->in=='e'||*p->in=='E') { put(p,*p->in++); if (*p->in=='+'||*p->in=='-') put(p,*p->in++); if (!digits(p)) return 0; }
    return 1;
}
static int literal(sc *p, const char *w) {
    size_t n = strlen(w);
    if (strncmp(p->in, w, n) != 0) return 0;
    memcpy(p->out, w, n); p->out += n; p->in += n;
    return 1;
}
/* Objects and arrays share one loop: ',' continues, the matching bracket
 * closes; object elements are "key": value pairs. */
static int value(sc *p) {
    skip_ws(p);
    if (*p->in == '{' || *p->in == '[') {
        char close = *p->in == '{' ? '}' : ']';
        put(p, *p->in++); skip_ws(p);
        if (*p->in == close) { put(p, *p->in++); return 1; } /* {} / [] */
        for (;;) {
            if (close == '}') { skip_ws(p); if (*p->in != '"' || !str_(p)) return 0; skip_ws(p); if (*p->in++ != ':') return 0; put(p, ':'); }
            if (!value(p)) return 0;
            skip_ws(p);
            char c = *p->in++;
            if (c == ',') { put(p, c); continue; }
            if (c == close) { put(p, c); return 1; }
            return 0;
        }
    }
    if (*p->in == '"') return str_(p);
    if (*p->in=='t'||*p->in=='f'||*p->in=='n') return literal(p, *p->in=='t'?"true":*p->in=='f'?"false":"null");
    return (*p->in=='-'||(*p->in>='0'&&*p->in<='9')) ? number(p) : 0;
}
/* Compact JSON serialization of input, or 0 for invalid JSON; out must hold
 * strlen(input)+1 bytes — the compact form is never longer than the input. */
int minify_json(const char *input, char *out) {
    sc p = { input, out };
    if (!value(&p)) return 0;
    skip_ws(&p);
    if (*p.in != '\0') return 0; /* one top-level value, then whitespace only */
    *p.out = '\0';
    return 1;
}
int main(void) {
    char buf[128];
    if (!minify_json("{\n  \"a\": 1,\n  \"b\": [\n    1,\n    2\n  ]\n}", buf)) return 1;
    puts(buf); /* {"a":1,"b":[1,2]} — and minify_json("{invalid}") returns 0 */
    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 →