diff --git a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/JsonWireSerializedValue.cs b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/JsonWireSerializedValue.cs index 6b97c8c3e2d..83b22363ea5 100644 --- a/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/JsonWireSerializedValue.cs +++ b/dotnet/src/Microsoft.Agents.AI.Workflows/Checkpointing/JsonWireSerializedValue.cs @@ -58,6 +58,9 @@ public override bool Equals(object? obj) public override int GetHashCode() { - return this.Data.GetHashCode(); + // JsonElement does not provide a structural hash code. ValueKind is necessarily equal + // whenever DeepEquals returns true, preserving the equality contract without recursively + // reimplementing JsonElement.DeepEquals semantics. + return this.Data.ValueKind.GetHashCode(); } } diff --git a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/JsonSerializationTests.cs b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/JsonSerializationTests.cs index 580ff8209db..c0cbb4433b5 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/JsonSerializationTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Workflows.UnitTests/JsonSerializationTests.cs @@ -33,6 +33,24 @@ private static JsonSerializerOptions TestCustomSerializedJsonOptions private static EdgeId TakeEdgeId() => new(Interlocked.Increment(ref s_nextEdgeId)); + [Fact] + public void Test_JsonWireSerializedValue_EqualValuesHaveEqualHashCodes() + { + // Arrange + JsonMarshaller marshaller = new(); + using JsonDocument firstDocument = JsonDocument.Parse("""{"a":1,"b":[true,null]}"""); + using JsonDocument secondDocument = JsonDocument.Parse("""{"a":1,"b":[true,null]}"""); + JsonWireSerializedValue first = new(marshaller, firstDocument.RootElement); + JsonWireSerializedValue second = new(marshaller, secondDocument.RootElement); + + // Act + bool areEqual = first.Equals(second); + + // Assert + areEqual.Should().BeTrue(); + first.GetHashCode().Should().Be(second.GetHashCode()); + } + internal static T RunJsonRoundtrip(T value, JsonSerializerOptions? externalOptions = null, Expression>? predicate = null) { JsonMarshaller marshaller = new(externalOptions);