diff --git a/lib/std/json.zig b/lib/std/json.zig index 590040efba..d2467fb2cd 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -1511,6 +1511,34 @@ fn parseInternal( } }, .Struct => |structInfo| { + if (structInfo.is_tuple) { + switch (token) { + .ArrayBegin => {}, + else => return error.UnexpectedToken, + } + var r: T = undefined; + var child_options = options; + child_options.allow_trailing_data = true; + var fields_seen: usize = 0; + errdefer { + inline for (0..structInfo.fields.len) |i| { + if (i < fields_seen) { + parseFree(structInfo.fields[i].type, r[i], options); + } + } + } + inline for (0..structInfo.fields.len) |i| { + r[i] = try parse(structInfo.fields[i].type, tokens, child_options); + fields_seen = i + 1; + } + const tok = (try tokens.next()) orelse return error.UnexpectedEndOfJson; + switch (tok) { + .ArrayEnd => {}, + else => return error.UnexpectedToken, + } + return r; + } + switch (token) { .ObjectBegin => {}, else => return error.UnexpectedToken, @@ -2290,7 +2318,7 @@ pub fn stringify( return value.jsonStringify(options, out_stream); } - try out_stream.writeByte('{'); + try out_stream.writeByte(if (S.is_tuple) '[' else '{'); var field_output = false; var child_options = options; if (child_options.whitespace) |*child_whitespace| { @@ -2320,11 +2348,13 @@ pub fn stringify( if (child_options.whitespace) |child_whitespace| { try child_whitespace.outputIndent(out_stream); } - try encodeJsonString(Field.name, options, out_stream); - try out_stream.writeByte(':'); - if (child_options.whitespace) |child_whitespace| { - if (child_whitespace.separator) { - try out_stream.writeByte(' '); + if (!S.is_tuple) { + try encodeJsonString(Field.name, options, out_stream); + try out_stream.writeByte(':'); + if (child_options.whitespace) |child_whitespace| { + if (child_whitespace.separator) { + try out_stream.writeByte(' '); + } } } try stringify(@field(value, Field.name), child_options, out_stream); @@ -2335,7 +2365,7 @@ pub fn stringify( try whitespace.outputIndent(out_stream); } } - try out_stream.writeByte('}'); + try out_stream.writeByte(if (S.is_tuple) ']' else '}'); return; }, .ErrorSet => return stringify(@as([]const u8, @errorName(value)), options, out_stream), @@ -2649,6 +2679,10 @@ test "stringify vector" { try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{}); } +test "stringify tuple" { + try teststringify("[\"foo\",42]", std.meta.Tuple(&.{ []const u8, usize }){ "foo", 42 }, StringifyOptions{}); +} + fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions) !void { const ValidationWriter = struct { const Self = @This(); diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig index 067bc2920b..ce4b4ea7e8 100644 --- a/lib/std/json/test.zig +++ b/lib/std/json/test.zig @@ -2459,6 +2459,56 @@ test "parse into struct ignoring unknown fields" { try testing.expectEqualSlices(u8, "zig", r.language); } +test "parse into tuple" { + const options = ParseOptions{ .allocator = testing.allocator }; + const Union = union(enum) { + char: u8, + float: f64, + string: []const u8, + }; + const T = std.meta.Tuple(&.{ + i64, + f64, + bool, + []const u8, + ?bool, + struct { + foo: i32, + bar: []const u8, + }, + std.meta.Tuple(&.{ u8, []const u8, u8 }), + Union, + }); + var ts = TokenStream.init( + \\[ + \\ 420, + \\ 3.14, + \\ true, + \\ "zig", + \\ null, + \\ { + \\ "foo": 1, + \\ "bar": "zero" + \\ }, + \\ [4, "två", 42], + \\ 12.34 + \\] + ); + const r = try parse(T, &ts, options); + defer parseFree(T, r, options); + try testing.expectEqual(@as(i64, 420), r[0]); + try testing.expectEqual(@as(f64, 3.14), r[1]); + try testing.expectEqual(true, r[2]); + try testing.expectEqualSlices(u8, "zig", r[3]); + try testing.expectEqual(@as(?bool, null), r[4]); + try testing.expectEqual(@as(i32, 1), r[5].foo); + try testing.expectEqualSlices(u8, "zero", r[5].bar); + try testing.expectEqual(@as(u8, 4), r[6][0]); + try testing.expectEqualSlices(u8, "två", r[6][1]); + try testing.expectEqual(@as(u8, 42), r[6][2]); + try testing.expectEqual(Union{ .float = 12.34 }, r[7]); +} + const ParseIntoRecursiveUnionDefinitionValue = union(enum) { integer: i64, array: []const ParseIntoRecursiveUnionDefinitionValue,