+36
-1
src/internal/json.zig
+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
+
}