| ... | ... | @@ -1573,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1573 | 1573 | // .UseLast => {}, |
| 1574 | 1574 | // } |
| 1575 | 1575 | 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; |
| 1576 | 1579 | break; |
| 1577 | 1580 | } else if (options.duplicate_field_behavior == .Error) { |
| 1578 | 1581 | return error.DuplicateJSONField; |
| 1579 | 1582 | } 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 | } |
| 1581 | 1586 | fields_seen[i] = false; |
| 1582 | 1587 | } |
| 1583 | 1588 | } |
| ... | ... | @@ -1731,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void { |
| 1731 | 1736 | }, |
| 1732 | 1737 | .Struct => |structInfo| { |
| 1733 | 1738 | 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 | } |
| 1735 | 1742 | } |
| 1736 | 1743 | }, |
| 1737 | 1744 | .Array => |arrayInfo| { |
| ... | ... | @@ -1908,14 +1915,19 @@ test "parse with comptime field" { |
| 1908 | 1915 | }, |
| 1909 | 1916 | }; |
| 1910 | 1917 | |
| 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( |
| 1912 | 1923 | \\{ |
| 1913 | 1924 | \\ "kind": "float", |
| 1914 | 1925 | \\ "b": 1.0 |
| 1915 | 1926 | \\} |
| 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); |
| 1919 | 1931 | } |
| 1920 | 1932 | } |
| 1921 | 1933 | |
| ... | ... | @@ -2002,17 +2014,33 @@ test "parse into struct with duplicate field" { |
| 2002 | 2014 | const ballast = try testing.allocator.alloc(u64, 1); |
| 2003 | 2015 | defer testing.allocator.free(ballast); |
| 2004 | 2016 | |
| 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{ |
| 2006 | 2023 | .allocator = testing.allocator, |
| 2007 | 2024 | .duplicate_field_behavior = .UseLast, |
| 2008 | 2025 | }; |
| 2026 | |
| 2009 | 2027 | const str = "{ \"a\": 1, \"a\": 0.25 }"; |
| 2010 | 2028 | |
| 2011 | 2029 | 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)); |
| 2013 | 2033 | |
| 2014 | 2034 | 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)); |
| 2016 | 2044 | } |
| 2017 | 2045 | |
| 2018 | 2046 | /// A non-stream JSON parser which constructs a tree of Value's. |