ajoino
December 5, 2024, 9:00pm
1
I’m deserializing some json which contains lists of strings, and I was wondering what would be the best way map that to a zig struct. My initial idea was to use std.ArrayLists
s, but that failed and I suspect it’s because std.ArrayList
lacks some jsonParse*
functions.
Example hidden because it's not super important
const std = @import("std");
// This doesn't work
const StringList = struct {
list: std.ArrayList([]const u8),
};
test StringList {
const input = (
\\ { "list": ["abc", "123"] }
);
const parsed = try std.json.parseFromSlice(
StringList,
std.testing.allocator,
input,
.{},
);
defer parsed.deinit();
try std.testing.expectEqualSlices(u8, "abc", parsed.value.list.items[0]);
}
The error message is somewhat opaque:
error: error union with payload of opaque type ‘anyopaque’ not allowed
but that’s beside the point.
I’m currently using slices of strings instead ([][]const u8
), but I’m wondering if there’s a better or more idiomatic choice here?
1 Like
Sze
December 5, 2024, 10:17pm
2
My first try would be to use []const []const u8
, but so far I haven’t really used json a lot with Zig.
2 Likes
As @Sze said, the idiomatic choice here is []const []const u8
.
However, if you really need an ArrayList
, you could write a wrapper that implements jsonParse
:
Example
test "ArrayList" {
const input =
\\{"items":["item1", "item2", "item3"]}
;
const expected: []const []const u8 = &.{ "item1", "item2", "item3" };
const parsed = try std.json.parseFromSlice(DataList, std.testing.allocator, input, .{});
defer parsed.deinit();
for (0..expected.len) |i| {
try std.testing.expectEqualSlices(u8, expected[i], parsed.value.items.inner.items[i]);
}
}
const std = @import("std");
fn ArrayListJson(T: type) type {
return struct {
inner: std.ArrayList(T),
pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) !@This() {
var list = std.ArrayList(T).init(allocator);
errdefer list.deinit();
if (try source.next() != .array_begin) {
return error.UnexpectedToken;
}
while (true) {
const token = try source.nextAlloc(allocator, options.allocate.?);
switch (token) {
inline .string, .allocated_string => |s| try list.append(s),
.array_end => break,
else => {
std.debug.print("reached unreachable with token {s}\n", .{@tagName(token)});
unreachable;
},
}
}
return .{ .inner = list };
}
};
}
const DataList = struct {
items: ArrayListJson([]const u8),
};
However, I would advise against this approach.
2 Likes
ajoino
December 7, 2024, 12:55pm
4
Yeah I’m not looking to implement my own jsonParse*
functions unless I have to, so it’s good to know I was on the right track!