Skip to content

Commit 895eb99

Browse files
fix ValueTuple source generation support (#58723)
Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com>
1 parent a92e3fd commit 895eb99

File tree

7 files changed

+47
-3
lines changed

7 files changed

+47
-3
lines changed

src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,9 @@ public override FieldInfo[] GetFields(BindingFlags bindingAttr)
311311
// we want a static field and this is not static
312312
(BindingFlags.Static & bindingAttr) != 0 && !fieldSymbol.IsStatic ||
313313
// we want an instance field and this is static or a constant
314-
(BindingFlags.Instance & bindingAttr) != 0 && (fieldSymbol.IsStatic || fieldSymbol.IsConst))
314+
(BindingFlags.Instance & bindingAttr) != 0 && (fieldSymbol.IsStatic || fieldSymbol.IsConst) ||
315+
// symbol represents an explicitly named tuple element
316+
fieldSymbol.IsExplicitlyNamedTupleElement)
315317
{
316318
continue;
317319
}

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Reflection;
56
using System.Text.Json.Serialization;
67
using System.Text.Json.Serialization.Metadata;
78

@@ -10,6 +11,7 @@ namespace System.Text.Json.SourceGeneration.Tests
1011
public interface ITestContext
1112
{
1213
public JsonSourceGenerationMode JsonSourceGenerationMode { get; }
14+
public bool IsIncludeFieldsEnabled => GetType().GetCustomAttribute<JsonSourceGenerationOptionsAttribute>()?.IncludeFields ?? false;
1315

1416
public JsonTypeInfo<Location> Location { get; }
1517
public JsonTypeInfo<NumberTypes> NumberTypes { get; }
@@ -30,6 +32,7 @@ public interface ITestContext
3032
public JsonTypeInfo<RealWorldContextTests.MyNestedClass.MyNestedNestedClass> MyNestedNestedClass { get; }
3133
public JsonTypeInfo<object[]> ObjectArray { get; }
3234
public JsonTypeInfo<string> String { get; }
35+
public JsonTypeInfo<(string Label1, int Label2, bool)> ValueTupleStringInt32Boolean { get; }
3336
public JsonTypeInfo<RealWorldContextTests.ClassWithEnumAndNullable> ClassWithEnumAndNullable { get; }
3437
public JsonTypeInfo<ClassWithCustomConverter> ClassWithCustomConverter { get; }
3538
public JsonTypeInfo<StructWithCustomConverter> StructWithCustomConverter { get; }

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests
2626
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))]
2727
[JsonSerializable(typeof(object[]))]
2828
[JsonSerializable(typeof(string))]
29+
[JsonSerializable(typeof((string Label1, int Label2, bool)))]
2930
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
3031
[JsonSerializable(typeof(ClassWithCustomConverter))]
3132
[JsonSerializable(typeof(StructWithCustomConverter))]
@@ -69,6 +70,7 @@ public override void EnsureFastPathGeneratedAsExpected()
6970
Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.Serialize);
7071
Assert.Null(MetadataAndSerializationContext.Default.SampleEnum.Serialize);
7172
Assert.Null(MetadataAndSerializationContext.Default.String.Serialize);
73+
Assert.NotNull(MetadataAndSerializationContext.Default.ValueTupleStringInt32Boolean.Serialize);
7274
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithEnumAndNullable.Serialize);
7375
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverter);
7476
Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverter);

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ namespace System.Text.Json.SourceGeneration.Tests
2525
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Metadata)]
2626
[JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
2727
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata)]
28+
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata)]
2829
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata)]
2930
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
3031
[JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
@@ -66,6 +67,7 @@ public override void EnsureFastPathGeneratedAsExpected()
6667
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
6768
Assert.Null(MetadataWithPerTypeAttributeContext.Default.SampleEnum.Serialize);
6869
Assert.Null(MetadataWithPerTypeAttributeContext.Default.String.Serialize);
70+
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize);
6971
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize);
7072
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize);
7173
Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize);
@@ -80,7 +82,7 @@ public override void EnsureFastPathGeneratedAsExpected()
8082
}
8183
}
8284

83-
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
85+
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata, IncludeFields = true)]
8486
[JsonSerializable(typeof(Location))]
8587
[JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation")]
8688
[JsonSerializable(typeof(NumberTypes))]
@@ -100,6 +102,7 @@ public override void EnsureFastPathGeneratedAsExpected()
100102
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))]
101103
[JsonSerializable(typeof(object[]))]
102104
[JsonSerializable(typeof(string))]
105+
[JsonSerializable(typeof((string Label1, int Label2, bool)))]
103106
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
104107
[JsonSerializable(typeof(ClassWithCustomConverter))]
105108
[JsonSerializable(typeof(StructWithCustomConverter))]
@@ -164,6 +167,7 @@ public override void EnsureFastPathGeneratedAsExpected()
164167
Assert.Null(MetadataContext.Default.ObjectArray.Serialize);
165168
Assert.Null(MetadataContext.Default.SampleEnum.Serialize);
166169
Assert.Null(MetadataContext.Default.String.Serialize);
170+
Assert.Null(MetadataContext.Default.ValueTupleStringInt32Boolean.Serialize);
167171
Assert.Null(MetadataContext.Default.ClassWithEnumAndNullable.Serialize);
168172
Assert.Null(MetadataContext.Default.ClassWithCustomConverter.Serialize);
169173
Assert.Null(MetadataContext.Default.StructWithCustomConverter.Serialize);

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace System.Text.Json.SourceGeneration.Tests
88
{
9+
[JsonSourceGenerationOptions(IncludeFields = true)]
910
[JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Metadata)]
1011
[JsonSerializable(typeof(RepeatedTypes.Location), TypeInfoPropertyName = "RepeatedLocation", GenerationMode = JsonSourceGenerationMode.Serialization)]
1112
[JsonSerializable(typeof(NumberTypes), GenerationMode = JsonSourceGenerationMode.Metadata)]
@@ -25,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests
2526
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
2627
[JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
2728
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
29+
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
2830
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
2931
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
3032
[JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
@@ -67,6 +69,7 @@ public override void EnsureFastPathGeneratedAsExpected()
6769
Assert.Null(MixedModeContext.Default.ObjectArray.Serialize);
6870
Assert.Null(MixedModeContext.Default.SampleEnum.Serialize);
6971
Assert.Null(MixedModeContext.Default.String.Serialize);
72+
Assert.NotNull(MixedModeContext.Default.ValueTupleStringInt32Boolean.Serialize);
7073
Assert.NotNull(MixedModeContext.Default.ClassWithEnumAndNullable.Serialize);
7174
Assert.Null(MixedModeContext.Default.ClassWithCustomConverter.Serialize);
7275
Assert.Null(MixedModeContext.Default.StructWithCustomConverter.Serialize);

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,31 @@ public virtual void RoundTripTypeNameClash()
110110
VerifyRepeatedLocation(expected, obj);
111111
}
112112

113+
[Fact]
114+
public virtual void RoundTripValueTuple()
115+
{
116+
bool isIncludeFieldsEnabled = DefaultContext.IsIncludeFieldsEnabled;
117+
118+
var tuple = (Label1: "string", Label2: 42, true);
119+
string expectedJson = isIncludeFieldsEnabled
120+
? "{\"Item1\":\"string\",\"Item2\":42,\"Item3\":true}"
121+
: "{}";
122+
123+
string json = JsonSerializer.Serialize(tuple, DefaultContext.ValueTupleStringInt32Boolean);
124+
Assert.Equal(expectedJson, json);
125+
126+
if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
127+
{
128+
// Deserialization not supported in fast path serialization only mode
129+
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize(json, DefaultContext.ValueTupleStringInt32Boolean));
130+
}
131+
else
132+
{
133+
var deserializedTuple = JsonSerializer.Deserialize(json, DefaultContext.ValueTupleStringInt32Boolean);
134+
Assert.Equal(isIncludeFieldsEnabled ? tuple : default, deserializedTuple);
135+
}
136+
}
137+
113138
[Fact]
114139
public virtual void RoundTripWithCustomConverter_Class()
115140
{

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace System.Text.Json.SourceGeneration.Tests
2626
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass))]
2727
[JsonSerializable(typeof(object[]))]
2828
[JsonSerializable(typeof(string))]
29+
[JsonSerializable(typeof((string Label1, int Label2, bool)))]
2930
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable))]
3031
[JsonSerializable(typeof(ClassWithCustomConverter))]
3132
[JsonSerializable(typeof(StructWithCustomConverter))]
@@ -61,6 +62,7 @@ internal partial class SerializationContext : JsonSerializerContext, ITestContex
6162
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
6263
[JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)]
6364
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)]
65+
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Serialization)]
6466
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)]
6567
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)]
6668
[JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)]
@@ -77,7 +79,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer
7779
public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Serialization;
7880
}
7981

80-
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
82+
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, IncludeFields = true)]
8183
[JsonSerializable(typeof(Location), GenerationMode = JsonSourceGenerationMode.Serialization)]
8284
[JsonSerializable(typeof(RepeatedTypes.Location), GenerationMode = JsonSourceGenerationMode.Serialization, TypeInfoPropertyName = "RepeatedLocation")]
8385
[JsonSerializable(typeof(NumberTypes), GenerationMode = JsonSourceGenerationMode.Serialization)]
@@ -97,6 +99,7 @@ internal partial class SerializationWithPerTypeAttributeContext : JsonSerializer
9799
[JsonSerializable(typeof(RealWorldContextTests.MyNestedClass.MyNestedNestedClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
98100
[JsonSerializable(typeof(object[]), GenerationMode = JsonSourceGenerationMode.Serialization)]
99101
[JsonSerializable(typeof(string), GenerationMode = JsonSourceGenerationMode.Serialization)]
102+
[JsonSerializable(typeof((string Label1, int Label2, bool)), GenerationMode = JsonSourceGenerationMode.Serialization)]
100103
[JsonSerializable(typeof(RealWorldContextTests.ClassWithEnumAndNullable), GenerationMode = JsonSourceGenerationMode.Serialization)]
101104
[JsonSerializable(typeof(ClassWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)]
102105
[JsonSerializable(typeof(StructWithCustomConverter), GenerationMode = JsonSourceGenerationMode.Serialization)]
@@ -144,6 +147,7 @@ public override void EnsureFastPathGeneratedAsExpected()
144147
Assert.NotNull(SerializationContext.Default.MyNestedNestedClass.Serialize);
145148
Assert.Null(SerializationContext.Default.ObjectArray.Serialize);
146149
Assert.Null(SerializationContext.Default.String.Serialize);
150+
Assert.NotNull(SerializationContext.Default.ValueTupleStringInt32Boolean.Serialize);
147151
Assert.NotNull(SerializationContext.Default.ClassWithEnumAndNullable.Serialize);
148152
Assert.Null(SerializationContext.Default.ClassWithCustomConverter.Serialize);
149153
Assert.Null(SerializationContext.Default.StructWithCustomConverter.Serialize);
@@ -411,6 +415,7 @@ public override void EnsureFastPathGeneratedAsExpected()
411415
Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
412416
Assert.Null(SerializationWithPerTypeAttributeContext.Default.SampleEnum.Serialize);
413417
Assert.Null(SerializationWithPerTypeAttributeContext.Default.String.Serialize);
418+
Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ValueTupleStringInt32Boolean.Serialize);
414419
Assert.NotNull(SerializationWithPerTypeAttributeContext.Default.ClassWithEnumAndNullable.Serialize);
415420
Assert.Null(SerializationWithPerTypeAttributeContext.Default.ClassWithCustomConverter.Serialize);
416421
Assert.Null(SerializationWithPerTypeAttributeContext.Default.StructWithCustomConverter.Serialize);

0 commit comments

Comments
 (0)