I am trying to parse a protocol that mixes plain text tokens and JSON input. Here is a test that demonstrates what I am trying to do:
test "Partial json stream parse" {
const T = struct {
x: ?bool,
y: ?bool,
};
const input =
\\foo {"x": true, "y": null} bar
;
var reader: std.Io.Reader = .fixed(input);
var arena: std.heap.ArenaAllocator = .init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
var json_reader: std.json.Reader = .init(alloc, &reader);
try std.testing.expectEqualSlices(u8, "foo", (try reader.takeDelimiter(' ')) orelse "");
try std.testing.expectEqual(T{ .x = true, .y = null }, try std.json.parseFromTokenSourceLeaky(
T,
alloc,
&json_reader,
.{},
));
}
This fails due to an assertion that the JSON reader has reached the end of the input:
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.1303+ee0a0f119/lib/std/json/Scanner.zig:1317:34: 0x10611ce in skipWhitespaceCheckEnd (std.zig)
if (self.stackHeight() == 0) return error.SyntaxError;
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.1303+ee0a0f119/lib/std/json/Scanner.zig:390:21: 0x106248d in next (std.zig)
if (try self.skipWhitespaceCheckEnd()) return .end_of_document;
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.1303+ee0a0f119/lib/std/json/Scanner.zig:1717:37: 0x1053f4e in next (std.zig)
else => |other_err| return other_err,
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.1303+ee0a0f119/lib/std/json/static.zig:151:32: 0x104939e in parseFromTokenSourceLeaky__anon_3166 (std.zig)
assert(.end_of_document == try scanner_or_reader.next());
^
/home/robby/src/zits/src/server/message_parser.zig:187:60: 0x1041753 in test.Partial json stream parse (message_parser.zig)
try std.testing.expectEqual(T{ .x = true, .y = null }, try std.json.parseFromTokenSourceLeaky(
^
If I replace input with:
// note missing 'bar' after the object
const input =
\\foo {"x": true, "y": null}
;
it runs fine.
This is not an assertion that I care about. What I care about is that the stackHeight() of the scanner has reached zero, but there does not seem to be a parser function that handles this. I looked at trying to collect the tokens manually in an ArrayList and terminating when I read a stackHeight of zero, but there does not seem to be a way to process a token source besides std.json.Scanner or std.json.Reader, or implementing them (which seems like an unnecessary effort).
If I knew the bounds of the JSON in the reader buffer, I could use std.json.parseFromSlice, but there does not seem to be a reasonable way to do that. (Even if I could do this, it would not be preferred since I would have to deal with running into the buffer boundary myself).
Has anyone run into this problem before? Have I just run into the boundary of what the standard library supports here?