Skip to content

JSON Validator — Zig source

Validate JSON and pinpoint errors with line and column numbers. Clear valid/invalid verdict plus the exact error location - runs entirely in your browser.

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

//! json-validator — Zig port using std.json's streaming scanner, ported from src/lib/jsonValidate.ts.
const std = @import("std");

/// Mirrors the TypeScript result shape; line/column 0 = no position.
pub const Result = struct {
    valid: bool,
    error_name: []const u8 = "",
    line: usize = 0,
    column: usize = 0,
};

/// Validate UTF-8 JSON. `alloc` backs scanner scratch space only.
/// The scanner walks the token stream; Diagnostics carry the 1-based line
/// and column at the failure point — the out-of-band equivalent of the JS
/// port's "at position N" message scan.
pub fn validate(alloc: std.mem.Allocator, text: []const u8) Result {
    if (std.mem.trim(u8, text, " \t\r\n").len == 0)
        return .{ .valid = false, .error_name = "Input is empty" };

    var scanner = std.json.Scanner.initCompleteInput(alloc, text);
    defer scanner.deinit();

    var diag = std.json.Diagnostics{};
    scanner.enableDiagnostics(&diag);

    while (true) {
        const token = scanner.next() catch |err| {
            return .{
                .valid = false,
                .error_name = @errorName(err),
                .line = diag.line,
                .column = diag.column,
            };
        };
        if (token == .end_of_document) break;
    }
    return .{ .valid = true };
}

pub fn main() !void {
    const samples = [_][]const u8{
        "{ \"a\": [1, 2, { \"b\": null }] }",
        "{\n  \"a\": ,\n}",
    };
    const out = std.io.getStdOut().writer();
    for (samples) |sample| {
        const result = validate(std.heap.page_allocator, sample);
        if (result.valid) {
            try out.print("valid\n", .{});
        } else if (result.line != 0) {
            try out.print("invalid: {s} (line {d}, column {d})\n", .{ result.error_name, result.line, result.column });
        } else {
            try out.print("invalid: {s}\n", .{result.error_name});
        }
    }
}

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 →