diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 2658b1df156406..6383b2e3c8daa0 100644 --- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -776,7 +776,18 @@ private void GenerateFastPathFuncForObject(SourceWriter writer, ContextGeneratio ? $"(({propertyGenSpec.DeclaringType.FullyQualifiedName}){ValueVarName})" : ValueVarName; - string propValueExpr = $"{objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode}"; + string propValueExpr; + if (defaultCheckType != DefaultCheckType.None) + { + // Use temporary variable to evaluate property value only once + string localVariableName = $"__value_{propertyGenSpec.NameSpecifiedInSourceCode}"; + writer.WriteLine($"{propertyGenSpec.PropertyType.FullyQualifiedName} {localVariableName} = {objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode};"); + propValueExpr = localVariableName; + } + else + { + propValueExpr = $"{objectExpr}.{propertyGenSpec.NameSpecifiedInSourceCode}"; + } switch (defaultCheckType) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs index 81be2c5d8e92c5..79403e2cb935f7 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs @@ -535,6 +535,7 @@ public class CustomWrappingResolver : IJsonTypeInfoResolver [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(JsonMessage))] + [JsonSerializable(typeof(AllocatingOnPropertyAccess))] public partial class FastPathSerializationContext : JsonSerializerContext { } @@ -816,5 +817,22 @@ public TestResolver(Func getTypeInfo public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) => _getTypeInfo(type, options); } + + [Fact] + public static void FastPathSerialization_EvaluatePropertyOnlyOnceWhenIgnoreNullOrDefaultIsSpecified() + { + JsonSerializerOptions options = FastPathSerializationContext.Default.Options; + JsonTypeInfo allocatingOnPropertyAccessInfo = (JsonTypeInfo)options.GetTypeInfo(typeof(AllocatingOnPropertyAccess)); + Assert.NotNull(allocatingOnPropertyAccessInfo.SerializeHandler); + + var value = new AllocatingOnPropertyAccess(); + Assert.Equal(0, value.WhenWritingNullAccessCounter); + Assert.Equal(0, value.WhenWritingDefaultAccessCounter); + + string expectedJson = """{"SomeAllocatingProperty":"Current Value: 1","SomeAllocatingProperty2":"Current Value: 1"}"""; + Assert.Equal(expectedJson, JsonSerializer.Serialize(value, options)); + Assert.Equal(1, value.WhenWritingNullAccessCounter); + Assert.Equal(1, value.WhenWritingDefaultAccessCounter); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs index fc37676c8c2177..be241a0271d329 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs @@ -154,6 +154,18 @@ public class JsonMessage public int Length => Message?.Length ?? 0; // Read-only property } + public class AllocatingOnPropertyAccess + { + public int WhenWritingNullAccessCounter = 0; + public int WhenWritingDefaultAccessCounter = 0; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string SomeAllocatingProperty => $"Current Value: {++WhenWritingNullAccessCounter}"; + + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string SomeAllocatingProperty2 => $"Current Value: {++WhenWritingDefaultAccessCounter}"; + } + internal struct MyStruct { } public struct PersonStruct