Hi all,
I built a small web tool that generates Zig structs from JSON/YAML/TOML/XML samples. It’s a companion to serde.zig, but it can also emit plain structs for std.json users.
Two output targets:
serde.zig: a struct with apub const serde = .{ ... }block, ready to drop in.plain: a regular struct usable withstd.json.parseFromSlice.
Example input:
json {"userId":1,"firstName":"Alice","email":"alice@example.com","lastSeenAt":"2026-01-01T10:00:00Z"}
Output, serde.zig target:
const serde = @import("serde");
pub const User = struct {
id: u8,
name: []const u8,
roles: []const []const u8,
profile: Profile,
};
pub const Profile = struct {
created_at: []const u8,
active: bool,
pub const serde = .{
.rename_all = serde.NamingConvention.camel_case,
};
};
What it infers from the samples:
- smallest int width that covers the observed values (
u8here), - optional fields when a key is missing or
nullin any sample, rename_allwhen one naming convention round-trips every field, otherwise per-fieldrename,- given several samples, sibling fields that share a shape and never co-occur get folded under
alias, deny_unknown_fieldsis one toggle away in the advanced panel.
Everything runs in the browser (cloudflare pages hosted), samples are not uploaded anywhere.
Would appreciate feedback on the inference defaults and anything that produces wrong output. XML in particular still has rough corners around attribute vs. element ambiguity, so real-world payloads that break it would help.