diff --git a/lib/std/json/dynamic.zig b/lib/std/json/dynamic.zig index a1849b0fed..7bacbd60d1 100644 --- a/lib/std/json/dynamic.zig +++ b/lib/std/json/dynamic.zig @@ -147,7 +147,19 @@ fn handleCompleteValue(stack: *Array, allocator: Allocator, source: anytype, val // stack: [..., .object] var object = &stack.items[stack.items.len - 1].object; - try object.put(key, value); + + const gop = try object.getOrPut(key); + if (gop.found_existing) { + switch (options.duplicate_field_behavior) { + .use_first => {}, + .@"error" => return error.DuplicateField, + .use_last => { + gop.value_ptr.* = value; + }, + } + } else { + gop.value_ptr.* = value; + } // This is an invalid state to leave the stack in, // so we have to process the next token before we return. diff --git a/lib/std/json/dynamic_test.zig b/lib/std/json/dynamic_test.zig index 4ac468f866..9e181dea9c 100644 --- a/lib/std/json/dynamic_test.zig +++ b/lib/std/json/dynamic_test.zig @@ -181,6 +181,34 @@ test "escaped characters" { try testing.expectEqualSlices(u8, obj.get("surrogatepair").?.string, "😂"); } +test "Value with duplicate fields" { + var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena_allocator.deinit(); + + const doc = + \\{ + \\ "abc": 0, + \\ "abc": 1 + \\} + ; + + try testing.expectError(error.DuplicateField, parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ + .duplicate_field_behavior = .@"error", + })); + + const first = try parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ + .duplicate_field_behavior = .use_first, + }); + try testing.expectEqual(@as(usize, 1), first.object.count()); + try testing.expectEqual(@as(i64, 0), first.object.get("abc").?.integer); + + const last = try parseFromSliceLeaky(std.json.Value, arena_allocator.allocator(), doc, .{ + .duplicate_field_behavior = .use_last, + }); + try testing.expectEqual(@as(usize, 1), last.object.count()); + try testing.expectEqual(@as(i64, 1), last.object.get("abc").?.integer); +} + test "Value.jsonStringify" { var vals = [_]Value{ .{ .integer = 1 }, diff --git a/lib/std/json/test.zig b/lib/std/json/test.zig index 9530ab37a6..136e8e34d1 100644 --- a/lib/std/json/test.zig +++ b/lib/std/json/test.zig @@ -28,7 +28,9 @@ fn testLowLevelScanner(s: []const u8) !void { } } fn testHighLevelDynamicParser(s: []const u8) !void { - var parsed = try parseFromSlice(Value, testing.allocator, s, .{}); + var parsed = try parseFromSlice(Value, testing.allocator, s, .{ + .duplicate_field_behavior = .use_first, + }); defer parsed.deinit(); }