atproto utils for zig zat.dev
atproto sdk zig

feat: extractAt ignores unknown JSON fields by default

enables partial extraction from JSON with extra fields, which is common
when parsing external API responses (e.g., TAP firehose messages have
live, rev, cid fields we don't need).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Changed files
+36 -1
src
internal
+36 -1
src/internal/json.zig
··· 125 125 }, 126 126 }; 127 127 } 128 - return std.json.parseFromValueLeaky(T, allocator, current, .{}) catch |err| { 128 + return std.json.parseFromValueLeaky(T, allocator, current, .{ .ignore_unknown_fields = true }) catch |err| { 129 129 log.debug("extractAt: parse failed for {s} at path {any}: {s} (json type: {s})", .{ 130 130 @typeName(T), 131 131 path, ··· 347 347 const result = extractAtOptional(Thing, arena.allocator(), parsed.value, .{ "data", "missing" }); 348 348 try std.testing.expect(result == null); 349 349 } 350 + 351 + test "extractAt ignores unknown fields" { 352 + // real-world case: TAP messages have extra fields (live, rev, cid) that we don't need 353 + const json_str = 354 + \\{ 355 + \\ "record": { 356 + \\ "live": true, 357 + \\ "did": "did:plc:abc123", 358 + \\ "rev": "3mbspmpaidl2a", 359 + \\ "collection": "pub.leaflet.document", 360 + \\ "rkey": "xyz789", 361 + \\ "action": "create", 362 + \\ "cid": "bafyreitest" 363 + \\ } 364 + \\} 365 + ; 366 + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); 367 + defer arena.deinit(); 368 + 369 + const parsed = try std.json.parseFromSlice(std.json.Value, arena.allocator(), json_str, .{}); 370 + 371 + // only extract the fields we care about 372 + const Record = struct { 373 + collection: []const u8, 374 + action: []const u8, 375 + did: []const u8, 376 + rkey: []const u8, 377 + }; 378 + 379 + const rec = try extractAt(Record, arena.allocator(), parsed.value, .{"record"}); 380 + try std.testing.expectEqualStrings("pub.leaflet.document", rec.collection); 381 + try std.testing.expectEqualStrings("create", rec.action); 382 + try std.testing.expectEqualStrings("did:plc:abc123", rec.did); 383 + try std.testing.expectEqualStrings("xyz789", rec.rkey); 384 + }