Basically something like this, parsing into the dynamic std.json.Value, then temporary arena-allocated slices, then finally longer-lived slices owned by array lists.
const std = @import("std");
test {
const S = struct {
foo: std.ArrayList(i32),
bar: std.ArrayList(f32),
};
const json =
\\{
\\ "foo": [1, 2, 3, 4, 5],
\\ "bar": [1.25, 573.765]
\\}
;
const gpa = std.testing.allocator;
var result: S = parse: {
var arena_state: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const dynamic = try std.json.parseFromSliceLeaky(std.json.Value, arena, json, .{});
if (dynamic != .object) return error.InvalidJson;
const dynamic_foo = dynamic.object.get("foo") orelse return error.InvalidJson;
if (dynamic_foo != .array) return error.InvalidJson;
const temp_foo_slice = try std.json.parseFromValueLeaky([]i32, arena, dynamic_foo, .{});
const owned_foo_slice = try gpa.dupe(i32, temp_foo_slice);
errdefer gpa.free(owned_foo_slice);
const dynamic_bar = dynamic.object.get("bar") orelse return error.InvalidJson;
if (dynamic_bar != .array) return error.InvalidJson;
const temp_bar_slice = try std.json.parseFromValueLeaky([]f32, arena, dynamic_bar, .{});
const owned_bar_slice = try gpa.dupe(f32, temp_bar_slice);
errdefer comptime unreachable;
break :parse .{
.foo = .fromOwnedSlice(owned_foo_slice),
.bar = .fromOwnedSlice(owned_bar_slice),
};
};
defer {
result.foo.deinit(gpa);
result.bar.deinit(gpa);
}
// Assert that the lists are initially at full capacity,
// but that expanding them by adding further items still works.
try std.testing.expect(result.foo.items.len == result.foo.capacity);
try std.testing.expect(result.bar.items.len == result.bar.capacity);
try result.foo.append(gpa, 999);
try result.bar.append(gpa, 55.55);
try std.testing.expectEqualSlices(i32, &.{ 1, 2, 3, 4, 5, 999 }, result.foo.items);
try std.testing.expectEqualSlices(f32, &.{ 1.25, 573.765, 55.55 }, result.bar.items);
}
There are probably better and more efficient ways of accomplishing this, such as using the low-level std.json.Scanner, but it gets the idea across. With clever use of @typeInfo and the isArrayList suggested above you can probably handle this in a generic manner that works for any struct. The sky’s the limit. The important detail is that the way std.json handles memory allocation means that you need to dupe allocated slices if you want to use them for array lists that may be resized in the future.