authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-14 15:32:32-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-05-14 15:32:32-04:00
log114c6612cb0709a317f941b49daa4d4849dc24e1
tree9a531106e63f3caa7ee25d633ee8966a1a02cf92
parent8f35c60b39dad1ecb1609f6105f1e41a52df87ae
parent11fd2aa76770a0bef87a6ce304e1893b490bba83
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8764 from mattbork/json-usefirst

Fix duplicate_field_behavior UseFirst for parse in json.zig

1 files changed, 37 insertions(+), 9 deletions(-)

lib/std/json.zig+37-9
......@@ -1573,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15731573 // .UseLast => {},
15741574 // }
15751575 if (options.duplicate_field_behavior == .UseFirst) {
1576 // unconditonally ignore value. for comptime fields, this skips check against default_value
1577 parseFree(field.field_type, try parse(field.field_type, tokens, options), options);
1578 found = true;
15761579 break;
15771580 } else if (options.duplicate_field_behavior == .Error) {
15781581 return error.DuplicateJSONField;
15791582 } else if (options.duplicate_field_behavior == .UseLast) {
1580 parseFree(field.field_type, @field(r, field.name), options);
1583 if (!field.is_comptime) {
1584 parseFree(field.field_type, @field(r, field.name), options);
1585 }
15811586 fields_seen[i] = false;
15821587 }
15831588 }
......@@ -1731,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
17311736 },
17321737 .Struct => |structInfo| {
17331738 inline for (structInfo.fields) |field| {
1734 parseFree(field.field_type, @field(value, field.name), options);
1739 if (!field.is_comptime) {
1740 parseFree(field.field_type, @field(value, field.name), options);
1741 }
17351742 }
17361743 },
17371744 .Array => |arrayInfo| {
......@@ -1908,14 +1915,19 @@ test "parse with comptime field" {
19081915 },
19091916 };
19101917
1911 const r = try std.json.parse(T, &std.json.TokenStream.init(
1918 const options = ParseOptions{
1919 .allocator = std.testing.allocator,
1920 };
1921
1922 const r = try parse(T, &TokenStream.init(
19121923 \\{
19131924 \\ "kind": "float",
19141925 \\ "b": 1.0
19151926 \\}
1916 ), .{
1917 .allocator = std.testing.allocator,
1918 });
1927 ), options);
1928
1929 // check that parseFree doesn't try to free comptime fields
1930 parseFree(T, r, options);
19191931 }
19201932}
19211933
......@@ -2002,17 +2014,33 @@ test "parse into struct with duplicate field" {
20022014 const ballast = try testing.allocator.alloc(u64, 1);
20032015 defer testing.allocator.free(ballast);
20042016
2005 const options = ParseOptions{
2017 const options_first = ParseOptions{
2018 .allocator = testing.allocator,
2019 .duplicate_field_behavior = .UseFirst
2020 };
2021
2022 const options_last = ParseOptions{
20062023 .allocator = testing.allocator,
20072024 .duplicate_field_behavior = .UseLast,
20082025 };
2026
20092027 const str = "{ \"a\": 1, \"a\": 0.25 }";
20102028
20112029 const T1 = struct { a: *u64 };
2012 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options));
2030 // both .UseFirst and .UseLast should fail because second "a" value isn't a u64
2031 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_first));
2032 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_last));
20132033
20142034 const T2 = struct { a: f64 };
2015 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options));
2035 try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first));
2036 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last));
2037
2038 const T3 = struct { comptime a: f64 = 1.0 };
2039 // .UseFirst should succeed because second "a" value is unconditionally ignored (even though != 1.0)
2040 const t3 = T3{ .a = 1.0 };
2041 try testing.expectEqual(t3, try parse(T3, &TokenStream.init(str), options_first));
2042 // .UseLast should fail because second "a" value is 0.25 which is not equal to default value of 1.0
2043 try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last));
20162044}
20172045
20182046/// A non-stream JSON parser which constructs a tree of Value's.