JSON to serde.zig
zigshape writes idiomatic snake_case Zig structs with a pub const serde block that serde.zig reads directly, so camelCase or kebab-case JSON maps without hand-written renames.
API responseSmallest int widths from observed range; auto enum suggestion; missing/null become optional.
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"roles": ["admin", "user"],
"profile": { "createdAt": "2026-01-01T10:00:00Z", "active": true }
}User.zig const serde = @import("serde");
pub const User = struct {
id: u8,
name: []const u8,
email: []const u8,
roles: []const []const u8,
profile: Profile,
};
pub const Profile = struct {
created_at: []const u8,
active: bool,
pub const serde = .{
.rename_all = .camel_case,
};
};
Diagnostics
2 warningsField decisions
Why each field got its type. Click a row to see it in the input; edit to override.
struct User $ 5 fields
struct Profile $.profile 2 fields
serde.zig options zigshape emits
.rename_all = .camel_case(or kebab, Pascal, SCREAMING_SNAKE) when one convention round-trips every key; per-field.renameotherwise..aliasfor sibling keys that share a shape and never appear in the same sample.- Tagged unions with
.tagand.tag_fieldwhen objects carry a discriminator, in any of serde.zig's four tagging styles. .deny_unknown_fields,.flattenand.skipfrom the Options panel or the CLI config, with defaults added for skipped fields.
Questions
- How do I parse JSON with the generated serde.zig struct?
var parsed = try serde.json.fromSliceManaged(User, allocator, input);thendefer parsed.deinit();. The result owns one arena, so a singledeinitfrees everything.- Which serde.zig version does the output target?
- serde.zig 1.2.3, which supports Zig 0.15.2, 0.16 and 0.17. The More menu copies the
zig fetchcommand for that release. - What happens with fields whose type can't be inferred?
- They become
?serde.Value = null. serde.zig readsnullinto it but doesn't parse arbitrary input intoserde.Value, so zigshape warns and suggests setting a concrete type.