diff --git a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs index 50d76bf0e2878e..71ea983e90ab13 100644 --- a/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs +++ b/src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs @@ -311,7 +311,9 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr) // we want a static field and this is not static (BindingFlags.Static & bindingAttr) != 0 && !fieldSymbol.IsStatic || // we want an instance field and this is static or a constant - (BindingFlags.Instance & bindingAttr) != 0 && (fieldSymbol.IsStatic || fieldSymbol.IsConst)) + (BindingFlags.Instance & bindingAttr) != 0 && (fieldSymbol.IsStatic || fieldSymbol.IsConst) || + // symbol represents an explicitly named tuple element + fieldSymbol.IsExplicitlyNamedTupleElement) { continue; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs index ab9352249395aa..994f73b9827252 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -10,6 +11,7 @@ namespace System.Text.Json.SourceGeneration.Tests public interface ITestContext { public JsonSourceGenerationMode JsonSourceGenerationMode { get; } + public bool IsIncludeFieldsEnabled => GetType().GetCustomAttribute()?.IncludeFields ?? false; public JsonTypeInfo Location { get; } public JsonTypeInfo NumberTypes { get; } @@ -30,6 +32,7 @@ public interface ITestContext public JsonTypeInfo MyNestedNestedClass { get; } public JsonTypeInfo ObjectArray { get; } public JsonTypeInfo String { get; } + public JsonTypeInfo<(string Label1, int Label2, bool)> ValueTupleStringInt32Boolean { get; } public JsonTypeInfo ClassWithEnumAndNullable { get; } public JsonTypeInfo ClassWithCustomConverter { get; } public JsonTypeInfo StructWithCustomConverter { get; } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs index 6db87ac740cae5..7ea47c8f7a4f10 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs @@ -26,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] [JsonSerializable(typeof(object[]))] [JsonSerializable(typeof(string))] + [JsonSerializable(typeof((string Label1, int Label2, bool)))] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))] [JsonSerializable(typeof(ClassWithCustomConverter))] [JsonSerializable(typeof(StructWithCustomConverter))] @@ -64,6 +65,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(MetadataAndSerializationContext.Default.MyNestedNestedClass.Serialize); Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.Serialize); Assert.Null(MetadataAndSerializationContext.Default.String.Serialize); + Assert.NotNull(MetadataAndSerializationContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithEnumAndNullable.Serialize); Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverter); Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverter); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs index 50f52d6f9aa6ef..b7835d367ab031 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs @@ -25,6 +25,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -63,6 +64,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize); Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.Serialize); + Assert.Null(MetadataWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize); Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize); @@ -71,7 +73,7 @@ public override void EnsureFastPathGeneratedAsExpected() } } - [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] + [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata, IncludeFields = true)] [JsonSerializable(typeof(Location))] [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")] [JsonSerializable(typeof(NumberTypes))] @@ -91,6 +93,7 @@ public override void EnsureFastPathGeneratedAsExpected() [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] [JsonSerializable(typeof(object[]))] [JsonSerializable(typeof(string))] + [JsonSerializable(typeof((string Label1, int Label2, bool)))] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))] [JsonSerializable(typeof(ClassWithCustomConverter))] [JsonSerializable(typeof(StructWithCustomConverter))] @@ -129,6 +132,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.Null(MetadataContext.Default.MyNestedNestedClass.Serialize); Assert.Null(MetadataContext.Default.ObjectArray.Serialize); Assert.Null(MetadataContext.Default.String.Serialize); + Assert.Null(MetadataContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.Null(MetadataContext.Default.ClassWithEnumAndNullable.Serialize); Assert.Null(MetadataContext.Default.ClassWithCustomConverter.Serialize); Assert.Null(MetadataContext.Default.StructWithCustomConverter.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs index d45996341f800c..94798456f26c8c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs @@ -6,6 +6,7 @@ namespace System.Text.Json.SourceGeneration.Tests { + [JsonSourceGenerationOptions(IncludeFields = true)] [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation", GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(NumberTypes), GenerationMode = JsonSourceGenerationMode.Metadata)] @@ -25,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)] @@ -62,6 +64,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(MixedModeContext.Default.MyNestedNestedClass.Serialize); Assert.Null(MixedModeContext.Default.ObjectArray.Serialize); Assert.Null(MixedModeContext.Default.String.Serialize); + Assert.NotNull(MixedModeContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.NotNull(MixedModeContext.Default.ClassWithEnumAndNullable.Serialize); Assert.Null(MixedModeContext.Default.ClassWithCustomConverter.Serialize); Assert.Null(MixedModeContext.Default.StructWithCustomConverter.Serialize); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 5fc9bf7fb73f1e..36cc695266b38a 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -110,6 +110,31 @@ public virtual void RoundTripTypeNameClash() VerifyRepeatedLocation(expected, obj); } + [Fact] + public virtual void RoundTripValueTuple() + { + bool isIncludeFieldsEnabled = DefaultContext.IsIncludeFieldsEnabled; + + var tuple = (Label1: "string", Label2: 42, true); + string expectedJson = isIncludeFieldsEnabled + ? "{\"Item1\":\"string\",\"Item2\":42,\"Item3\":true}" + : "{}"; + + string json = JsonSerializer.Serialize(tuple, DefaultContext.ValueTupleStringInt32Boolean); + Assert.Equal(expectedJson, json); + + if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization) + { + // Deserialization not supported in fast path serialization only mode + Assert.Throws(() => JsonSerializer.Deserialize(json, DefaultContext.ValueTupleStringInt32Boolean)); + } + else + { + var deserializedTuple = JsonSerializer.Deserialize(json, DefaultContext.ValueTupleStringInt32Boolean); + Assert.Equal(isIncludeFieldsEnabled ? tuple : default, deserializedTuple); + } + } + [Fact] public virtual void RoundTripWithCustomConverter_Class() { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs index ae87218f2f7825..94a257d1f357e1 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs @@ -26,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))] [JsonSerializable(typeof(object[]))] [JsonSerializable(typeof(string))] + [JsonSerializable(typeof((string Label1, int Label2, bool)))] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))] [JsonSerializable(typeof(ClassWithCustomConverter))] [JsonSerializable(typeof(StructWithCustomConverter))] @@ -57,6 +58,7 @@ internal partial class SerializationContext : JsonSerializerContext, ITestContex [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -69,7 +71,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Serialization; } - [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] + [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, IncludeFields = true)] [JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")] [JsonSerializable(typeof(NumberTypes), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -89,6 +91,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer [JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)] + [JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)] @@ -132,6 +135,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(SerializationContext.Default.MyNestedNestedClass.Serialize); Assert.Null(SerializationContext.Default.ObjectArray.Serialize); Assert.Null(SerializationContext.Default.String.Serialize); + Assert.NotNull(SerializationContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.NotNull(SerializationContext.Default.ClassWithEnumAndNullable.Serialize); Assert.Null(SerializationContext.Default.ClassWithCustomConverter.Serialize); Assert.Null(SerializationContext.Default.StructWithCustomConverter.Serialize); @@ -396,6 +400,7 @@ public override void EnsureFastPathGeneratedAsExpected() Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.MyNestedNestedClass.Serialize); Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize); Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.Serialize); + Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize); Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize); Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize); Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize);