| author | |
| committer | |
| log | 8f7b50e2c4ad0c08015bce02a3ba8a7a9a058e11 |
| tree | f0b56e1d62c04164cefdc8a47b98e561f50248a4 |
| parent | fe66a12a23eda90b89b06dc28957f3baac01ee89 |
3 files changed, 44 insertions(+), 2 deletions(-)
lib/std/json/dynamic.zig+13-1| ... | @@ -147,7 +147,19 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val | ... | @@ -147,7 +147,19 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val |
| 147 | 147 | ||
| 148 | // stack: [..., .object] | 148 | // stack: [..., .object] |
| 149 | var object = &stack.items[stack.items.len - 1].object; | 149 | var object = &stack.items[stack.items.len - 1].object; |
| 150 | try object.put(key, value); | 150 | |
| 151 | const gop = try object.getOrPut(key); | ||
| 152 | if (gop.found_existing) { | ||
| 153 | switch (options.duplicate_field_behavior) { | ||
| 154 | .use_first => {}, | ||
| 155 | .@"error" => return error.DuplicateField, | ||
| 156 | .use_last => { | ||
| 157 | gop.value_ptr.* = value; | ||
| 158 | }, | ||
| 159 | } | ||
| 160 | } else { | ||
| 161 | gop.value_ptr.* = value; | ||
| 162 | } | ||
| 151 | 163 | ||
| 152 | // This is an invalid state to leave the stack in, | 164 | // This is an invalid state to leave the stack in, |
| 153 | // so we have to process the next token before we return. | 165 | // so we have to process the next token before we return. |
lib/std/json/dynamic_test.zig+28| ... | @@ -181,6 +181,34 @@ test "escaped characters" { | ... | @@ -181,6 +181,34 @@ test "escaped characters" { |
| 181 | try testing.expectEqualSlices(u8, obj.get("surrogatepair").?.string, "😂"); | 181 | try testing.expectEqualSlices(u8, obj.get("surrogatepair").?.string, "😂"); |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | test "Value with duplicate fields" { | ||
| 185 | var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); | ||
| 186 | defer arena_allocator.deinit(); | ||
| 187 | |||
| 188 | const doc = | ||
| 189 | \\{ | ||
| 190 | \\ "abc": 0, | ||
| 191 | \\ "abc": 1 | ||
| 192 | \\} | ||
| 193 | ; | ||
| 194 | |||
| 195 | try testing.expectError(error.DuplicateField, parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ | ||
| 196 | .duplicate_field_behavior = .@"error", | ||
| 197 | })); | ||
| 198 | |||
| 199 | const first = try parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ | ||
| 200 | .duplicate_field_behavior = .use_first, | ||
| 201 | }); | ||
| 202 | try testing.expectEqual(@as(usize, 1), first.object.count()); | ||
| 203 | try testing.expectEqual(@as(i64, 0), first.object.get("abc").?.integer); | ||
| 204 | |||
| 205 | const last = try parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ | ||
| 206 | .duplicate_field_behavior = .use_last, | ||
| 207 | }); | ||
| 208 | try testing.expectEqual(@as(usize, 1), last.object.count()); | ||
| 209 | try testing.expectEqual(@as(i64, 1), last.object.get("abc").?.integer); | ||
| 210 | } | ||
| 211 | |||
| 184 | test "Value.jsonStringify" { | 212 | test "Value.jsonStringify" { |
| 185 | var vals = [_]Value{ | 213 | var vals = [_]Value{ |
| 186 | .{ .integer = 1 }, | 214 | .{ .integer = 1 }, |
lib/std/json/test.zig+3-1| ... | @@ -28,7 +28,9 @@ fn testLowLevelScanner(s: []const u8) !void { | ... | @@ -28,7 +28,9 @@ fn testLowLevelScanner(s: []const u8) !void { |
| 28 | } | 28 | } |
| 29 | } | 29 | } |
| 30 | fn testHighLevelDynamicParser(s: []const u8) !void { | 30 | fn testHighLevelDynamicParser(s: []const u8) !void { |
| 31 | var parsed = try parseFromSlice(Value, testing.allocator, s, .{}); | 31 | var parsed = try parseFromSlice(Value, testing.allocator, s, .{ |
| 32 | .duplicate_field_behavior = .use_first, | ||
| 33 | }); | ||
| 32 | defer parsed.deinit(); | 34 | defer parsed.deinit(); |
| 33 | } | 35 | } |
| 34 | 36 |