From d0b43063fd56057077b069e0b6ed838d0e0f4ef0 Mon Sep 17 00:00:00 2001 From: Pranav Senthilnathan Date: Tue, 25 Aug 2026 17:09:56 -0700 Subject: [PATCH] Throw JsonException for duplicate JsonObject properties Use the existing duplicate-property throw helper when eager JsonNode deserialization encounters a duplicate property. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1edde363-438d-4e24-aa0a-ed4256e32a36 --- .../Converters/Node/JsonObjectConverter.cs | 6 ++++-- .../JsonNode/JsonObjectTests.cs | 20 +++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs index 3ba1988198cae1..7136d63dd3adde 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Node/JsonObjectConverter.cs @@ -103,8 +103,10 @@ internal static JsonObject ReadAsJsonNode(ref Utf8JsonReader reader, JsonNodeOpt reader.Read(); // Move to the value token. JsonNode? value = JsonNodeConverter.ReadAsJsonNode(ref reader, options); - // To have parity with the lazy JsonObject, we throw on duplicates. - jObject.Add(propertyName, value); + if (!jObject.TryAdd(propertyName, value)) + { + ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName); + } } // JSON is invalid so reader would have already thrown. diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs index fdffaff5b6a0c9..f9d01d456c4d9c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonObjectTests.cs @@ -1736,9 +1736,9 @@ private static void AssertDuplicatePropertyThrows(string jsonPayload, bool is } else { - AssertExtensions.ThrowsContains( + AssertExtensions.ThrowsContains( () => JsonSerializer.Deserialize(jsonPayload, JsonTestSerializerOptions.DisallowDuplicateProperties), - "An item with the same key has already been added."); + "Duplicate property"); // Default options don't throw on deserialize but will throw when accessed T node = JsonSerializer.Deserialize(jsonPayload); @@ -1756,14 +1756,14 @@ public static void JsonObject_DuplicatePropertyCaseInsensitiveThrows() string jsonPayload = """{"a":1,"A":2}"""; _ = JsonSerializer.Deserialize(jsonPayload); // Assert no throw - AssertExtensions.ThrowsContains( + AssertExtensions.ThrowsContains( () => JsonSerializer.Deserialize(jsonPayload, options), - "An item with the same key has already been added."); + "Duplicate property"); _ = JsonSerializer.Deserialize(jsonPayload); // Assert no throw - AssertExtensions.ThrowsContains( + AssertExtensions.ThrowsContains( () => JsonSerializer.Deserialize(jsonPayload, options), - "An item with the same key has already been added."); + "Duplicate property"); } [Fact] @@ -1774,14 +1774,14 @@ public static void JsonObject_NestedDuplicatePropertyCaseInsensitiveThrows() string jsonPayload = """[{"a":1,"A":2}]"""; _ = JsonSerializer.Deserialize(jsonPayload); // Assert no throw - AssertExtensions.ThrowsContains( + AssertExtensions.ThrowsContains( () => JsonSerializer.Deserialize(jsonPayload, options), - "An item with the same key has already been added."); + "Duplicate property"); _ = JsonSerializer.Deserialize(jsonPayload); // Assert no throw - AssertExtensions.ThrowsContains( + AssertExtensions.ThrowsContains( () => JsonSerializer.Deserialize(jsonPayload, options), - "An item with the same key has already been added."); + "Duplicate property"); } [Theory]