| ... | @@ -1571,6 +1571,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: | ... | @@ -1571,6 +1571,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1571 | return error.DuplicateJSONField; | 1571 | return error.DuplicateJSONField; |
| 1572 | } else if (options.duplicate_field_behavior == .UseLast) { | 1572 | } else if (options.duplicate_field_behavior == .UseLast) { |
| 1573 | parseFree(field.field_type, @field(r, field.name), options); | 1573 | parseFree(field.field_type, @field(r, field.name), options); |
| | 1574 | fields_seen[i] = false; |
| 1574 | } | 1575 | } |
| 1575 | } | 1576 | } |
| 1576 | if (field.is_comptime) { | 1577 | if (field.is_comptime) { |
| ... | @@ -1642,6 +1643,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: | ... | @@ -1642,6 +1643,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: |
| 1642 | switch (ptrInfo.size) { | 1643 | switch (ptrInfo.size) { |
| 1643 | .One => { | 1644 | .One => { |
| 1644 | const r: T = try allocator.create(ptrInfo.child); | 1645 | const r: T = try allocator.create(ptrInfo.child); |
| | 1646 | errdefer allocator.destroy(r); |
| 1645 | r.* = try parseInternal(ptrInfo.child, token, tokens, options); | 1647 | r.* = try parseInternal(ptrInfo.child, token, tokens, options); |
| 1646 | return r; | 1648 | return r; |
| 1647 | }, | 1649 | }, |
| ... | @@ -1988,6 +1990,24 @@ test "parse into struct with misc fields" { | ... | @@ -1988,6 +1990,24 @@ test "parse into struct with misc fields" { |
| 1988 | try testing.expectEqual(T.Union{ .float = 100000 }, r.a_union); | 1990 | try testing.expectEqual(T.Union{ .float = 100000 }, r.a_union); |
| 1989 | } | 1991 | } |
| 1990 | | 1992 | |
| | 1993 | test "parse into struct with duplicate field" { |
| | 1994 | // allow allocator to detect double frees by keeping bucket in use |
| | 1995 | const ballast = try testing.allocator.alloc(u64, 1); |
| | 1996 | defer testing.allocator.free(ballast); |
| | 1997 | |
| | 1998 | const options = ParseOptions{ |
| | 1999 | .allocator = testing.allocator, |
| | 2000 | .duplicate_field_behavior = .UseLast, |
| | 2001 | }; |
| | 2002 | const str = "{ \"a\": 1, \"a\": 0.25 }"; |
| | 2003 | |
| | 2004 | const T1 = struct { a: *u64 }; |
| | 2005 | testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options)); |
| | 2006 | |
| | 2007 | const T2 = struct { a: f64 }; |
| | 2008 | testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options)); |
| | 2009 | } |
| | 2010 | |
| 1991 | /// A non-stream JSON parser which constructs a tree of Value's. | 2011 | /// A non-stream JSON parser which constructs a tree of Value's. |
| 1992 | pub const Parser = struct { | 2012 | pub const Parser = struct { |
| 1993 | allocator: *Allocator, | 2013 | allocator: *Allocator, |