JSON Formatter — Zig source
Beautify, minify and validate JSON instantly. Paste, format, copy - all in your browser. Share a link to your exact input.
This is the Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
// json-formatter — parse, pretty-print (2-space indent), and minify.
//
// Language: Zig (Zig 0.13, standard library: std.json)
// Source: CosmoDev polyglot showcase port of the `json-formatter` tool,
// ported from src/tools/JsonFormatter.tsx (the canonical
// TypeScript island).
// License: display source — part of CosmoDev's polyglot tool pages.
//
// Unlike the Rust/C/C++/Java/Swift ports (hand-written parsers), Zig's
// standard library ships a full JSON engine, so this port delegates to it:
// std.json.Value.object is a StringArrayHashMap, which preserves source
// insertion order just like a JavaScript engine, and std.json.stringify
// emits either compact output (the default) or the indented shape (": "
// after keys, "[]" / "{}" for empty containers, matching the island's
// JSON.stringify(parsed, null, 2)).
//
// Parity caveats: Value holds integers as i64 — exact up to 2^63, wider
// than JS Number's 2^53 — and doubles via Zig's shortest-round-trip float
// printing, which can differ from ECMAScript's Number::toString near the
// exponential-notation thresholds. std.json.stringify offers fixed indent
// widths (1/2/3/4 spaces), so a custom `indent` count maps onto the
// nearest supported level, defaulting to 2 (the UI's value). Parse errors
// are reported as the error name plus the byte offset from
// std.json.Diagnostics.
const std = @import("std");
/// Selects the output shape.
pub const Mode = enum { beautify, minify };
/// Mirrors the TypeScript { output, error } shape. `error_message` is null
/// on success or empty input ("error" alone is a Zig keyword).
/// All returned slices are allocated from the caller's arena.
pub const FormatResult = struct {
output: []const u8,
error_message: ?[]const u8 = null,
};
/// Map the requested indent width onto std.json's fixed levels, defaulting
/// to 2 (the UI's value).
fn whitespaceFor(indent: u8) std.json.StringifyOptions.Whitespace {
return switch (indent) {
1 => .indent_1,
3 => .indent_3,
4 => .indent_4,
else => .indent_2,
};
}
fn outOfMemory() FormatResult {
return .{ .output = "", .error_message = "out of memory" };
}
/// Parse and re-emit a JSON document the way the live island does.
/// `indent` is the number of spaces per nesting level for `.beautify`
/// (pass 2 to match the UI). Empty/whitespace-only input yields empty
/// output, no error. `arena` owns every allocation this function makes.
pub fn formatJson(arena: std.mem.Allocator, input: []const u8, mode: Mode, indent: u8) FormatResult {
const trimmed = std.mem.trim(u8, input, " \t\r\n");
if (trimmed.len == 0) return .{ .output = "" };
var diagnostics = std.json.Diagnostics{};
const parsed = std.json.parseFromSliceLeaky(std.json.Value, arena, trimmed, .{
.diagnostics = &diagnostics,
}) catch |err| {
// The error name plus position — the feel of the island's e.message.
const message = std.fmt.allocPrint(
arena,
"{s} at byte {d}",
.{ @errorName(err), diagnostics.byte_offset },
) catch return outOfMemory();
return .{ .output = "", .error_message = message };
};
var out = std.ArrayList(u8).init(arena);
const writer = out.writer();
switch (mode) {
.minify => std.json.stringify(parsed, .{}, writer) catch return outOfMemory(),
.beautify => std.json.stringify(parsed, .{ .whitespace = whitespaceFor(indent) }, writer) catch return outOfMemory(),
}
return .{ .output = out.items };
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var arena_state = std.heap.ArenaAllocator.init(gpa.allocator());
defer arena_state.deinit();
const arena = arena_state.allocator();
const sample = "{\"name\":\"CosmoDev\",\"tools\":[\"json\",\"base64\"],\"free\":true,\"nested\":{\"ok\":1}}";
const result = formatJson(arena, sample, .beautify, 2);
if (result.error_message) |message| {
std.debug.print("error: {s}\n", .{message});
} else {
std.debug.print("{s}\n", .{result.output});
}
}
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 →