Skip to content

Commit

Permalink
fix ValueTuple source generation support (#58644)
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis authored Sep 6, 2021
1 parent e4a8468 commit dff1c70
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 3 deletions.
4 changes: 3 additions & 1 deletion src/libraries/System.Text.Json/gen/Reflection/TypeWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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

public JsonTypeInfo<Location> Location { get; }
public JsonTypeInfo<NumberTypes> NumberTypes { get; }
Expand All @@ -30,6 +32,7 @@ public interface ITestContext
public JsonTypeInfo<RealWorldContextTests.MyNestedClass.MyNestedNestedClass> MyNestedNestedClass { get; }
public JsonTypeInfo<object[]> ObjectArray { get; }
public JsonTypeInfo<string> String { get; }
public JsonTypeInfo<(string Label1, int Label2, bool)> ValueTupleStringInt32Boolean { get; }
public JsonTypeInfo<RealWorldContextTests.ClassWithEnumAndNullable> ClassWithEnumAndNullable { get; }
public JsonTypeInfo<ClassWithCustomConverter> ClassWithCustomConverter { get; }
public JsonTypeInfo<StructWithCustomConverter> StructWithCustomConverter { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -69,6 +70,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataAndSerializationContext.Default.ObjectArray.Serialize);
Assert.Null(MetadataAndSerializationContext.Default.SampleEnum.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -66,6 +67,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.SampleEnum.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);
Expand All @@ -80,7 +82,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))]
Expand All @@ -100,6 +102,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))]
Expand Down Expand Up @@ -164,6 +167,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataContext.Default.ObjectArray.Serialize);
Assert.Null(MetadataContext.Default.SampleEnum.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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)]
Expand Down Expand Up @@ -67,6 +69,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MixedModeContext.Default.ObjectArray.Serialize);
Assert.Null(MixedModeContext.Default.SampleEnum.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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<InvalidOperationException>(() => 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()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand Down Expand Up @@ -61,6 +62,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)]
Expand All @@ -77,7 +79,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)]
Expand All @@ -97,6 +99,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)]
Expand Down Expand Up @@ -144,6 +147,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);
Expand Down Expand Up @@ -411,6 +415,7 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(SerializationWithPerTypeAttributeContext.Default.ObjectArray.Serialize);
Assert.Null(SerializationWithPerTypeAttributeContext.Default.SampleEnum.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);
Expand Down

0 comments on commit dff1c70

Please sign in to comment.