JSON Minify — Zig 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 Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
//! json-minify — compact JSON to its smallest valid form. Zig port (canonical
//! TS: src/lib/json-minify.ts; Go twin: cli/json-minify). No stdlib JSON
const std = @import("std");
const Scan = struct {
src: []const u8, out: []u8, i: usize = 0, o: usize = 0,
fn peek(s: *Scan) u8 { return if (s.i < s.src.len) s.src[s.i] else 0; }
fn take(s: *Scan) u8 { const c = s.peek(); s.i += 1; return c; } // 0 at end-of-input
fn skipWs(s: *Scan) void { while (switch (s.peek()) { ' ', '\t', '\n', '\r' => true, else => false }) s.i += 1; }
fn put(s: *Scan, c: u8) void { s.out[s.o] = c; s.o += 1; }
fn digits(s: *Scan) bool { var any = false; while (s.peek() >= '0' and s.peek() <= '9') { s.i += 1; any = true; } return any; }
fn string(s: *Scan) bool {
s.put('"'); s.i += 1; // opening quote
while (true) {
const c = s.take();
if (c == '"') { s.put('"'); return true; }
if (c == '\\') {
const e = s.take(); s.put(e);
if (e == 'u') {
for (0..4) |_| { const h = s.take(); if (!isHex(h)) return false; s.put(h); }
} else if (std.mem.indexOfScalar(u8, "\"\\/bfnrt", e) == null) return false; // 0 is not in the set
} else if (c < 0x20) return false // control byte / EOF
else s.put(c);
}
}
fn isHex(h: u8) bool { return (h >= '0' and h <= '9') or (h >= 'a' and h <= 'f') or (h >= 'A' and h <= 'F'); }
fn number(s: *Scan) bool {
const start = s.i;
if (s.peek() == '-') s.i += 1;
if (s.peek() == '0') s.i += 1 // "01" is rejected by the caller
else if (s.peek() >= '1' and s.peek() <= '9') { s.i += 1; while (s.peek() >= '0' and s.peek() <= '9') s.i += 1; }
else return false; // bare '-'
if (s.peek() == '.') { s.i += 1; if (!s.digits()) return false; }
if (s.peek() == 'e' or s.peek() == 'E') { s.i += 1; if (s.peek() == '+' or s.peek() == '-') s.i += 1; if (!s.digits()) return false; }
const n = s.i - start;
@memcpy(s.out[s.o .. s.o + n], s.src[start..s.i]); s.o += n;
return true;
}
fn literal(s: *Scan, w: []const u8) bool {
if (s.i > s.src.len or s.src.len - s.i < w.len) return false;
if (!std.mem.eql(u8, s.src[s.i .. s.i + w.len], w)) return false;
@memcpy(s.out[s.o .. s.o + w.len], w); s.i += w.len; s.o += w.len;
return true;
}
fn value(s: *Scan) bool {
s.skipWs();
const c = s.peek();
if (c == '{' or c == '[') {
const close: u8 = if (c == '{') '}' else ']';
s.put(c); s.i += 1; s.skipWs();
if (s.peek() == close) { s.put(close); s.i += 1; return true; } // {} / []
while (true) {
if (close == '}') { s.skipWs(); if (s.peek() != '"' or !s.string()) return false; s.skipWs(); if (s.peek() != ':') return false; s.i += 1; s.put(':'); }
if (!s.value()) return false;
s.skipWs();
const d = s.take();
if (d == ',') { s.put(d); continue; }
if (d == close) { s.put(close); return true; }
return false;
}
}
if (c == '"') return s.string();
if (c == 't' or c == 'f' or c == 'n') return s.literal(if (c == 't') "true" else if (c == 'f') "false" else "null");
return (c == '-' or (c >= '0' and c <= '9')) and s.number();
}
};
pub fn minify(alloc: std.mem.Allocator, input: []const u8) ?[]u8 {
const buf = alloc.alloc(u8, input.len) catch return null;
var s = Scan{ .src = input, .out = buf };
if (!s.value()) { alloc.free(buf); return null; }
s.skipWs();
if (s.i != s.src.len) { alloc.free(buf); return null; } // trailing garbage
return buf[0..s.o];
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const min = minify(arena.allocator(), "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}") orelse return error.Invalid;
std.debug.print("{s}\n", .{min}); // {"a":1,"b":[1,2]}
if (minify(arena.allocator(), "{invalid}") != null) return error.Invalid; // invalid JSON -> null
}
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 →