Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix custom JsonConverterFactories not working with transitive type/pr…
…operty declarations.
  • Loading branch information
eiriktsarpalis committed Dec 9, 2021
commit 5b9c9f5f8de8ca46c84fc6d747b1968e61576269
24 changes: 12 additions & 12 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ private sealed partial class Emitter

private readonly HashSet<string> _emittedPropertyFileNames = new();

private bool _generateGetConverterMethodForTypes = false;
private bool _generateGetConverterMethodForProperties = false;

public Emitter(in JsonSourceGenerationContext sourceGenerationContext, SourceGenerationSpec generationSpec)
{
_sourceGenerationContext = sourceGenerationContext;
Expand All @@ -107,24 +110,20 @@ public void Emit()
foreach (ContextGenerationSpec contextGenerationSpec in _generationSpec.ContextGenerationSpecList)
{
_currentContext = contextGenerationSpec;

bool generateGetConverterMethodForTypes = false;
bool generateGetConverterMethodForProperties = false;
_generateGetConverterMethodForTypes = false;
_generateGetConverterMethodForProperties = false;

foreach (TypeGenerationSpec typeGenerationSpec in _currentContext.RootSerializableTypes)
{
GenerateTypeInfo(typeGenerationSpec);

generateGetConverterMethodForTypes |= typeGenerationSpec.HasTypeFactoryConverter;
generateGetConverterMethodForProperties |= typeGenerationSpec.HasPropertyFactoryConverters;
}

string contextName = _currentContext.ContextType.Name;

// Add root context implementation.
AddSource(
$"{contextName}.g.cs",
GetRootJsonContextImplementation(generateGetConverterMethodForTypes, generateGetConverterMethodForProperties),
GetRootJsonContextImplementation(),
isRootContextDef: true);

// Add GetJsonTypeInfo override implementation.
Expand Down Expand Up @@ -297,6 +296,9 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec)
Location location = typeGenerationSpec.AttributeLocation ?? _currentContext.Location;
_sourceGenerationContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, location, new string[] { typeGenerationSpec.TypeInfoPropertyName }));
}

_generateGetConverterMethodForTypes |= typeGenerationSpec.HasTypeFactoryConverter;
_generateGetConverterMethodForProperties |= typeGenerationSpec.HasPropertyFactoryConverters;
}

private string GenerateForTypeWithKnownConverter(TypeGenerationSpec typeMetadata)
Expand Down Expand Up @@ -1140,9 +1142,7 @@ private string WrapWithCheckForCustomConverter(string source, string typeCompila
{IndentSource(source, numIndentations: 1)}
}}";

private string GetRootJsonContextImplementation(
bool generateGetConverterMethodForTypes,
bool generateGetConverterMethodForProperties)
private string GetRootJsonContextImplementation()
{
string contextTypeRef = _currentContext.ContextTypeRef;
string contextTypeName = _currentContext.ContextType.Name;
Expand All @@ -1166,12 +1166,12 @@ private string GetRootJsonContextImplementation(

{GetFetchLogicForRuntimeSpecifiedCustomConverter()}");

if (generateGetConverterMethodForProperties)
if (_generateGetConverterMethodForProperties)
{
sb.Append(GetFetchLogicForGetCustomConverter_PropertiesWithFactories());
}

if (generateGetConverterMethodForProperties || generateGetConverterMethodForTypes)
if (_generateGetConverterMethodForProperties || _generateGetConverterMethodForTypes)
{
sb.Append(GetFetchLogicForGetCustomConverter_TypesWithFactories());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ public interface ITestContext
public JsonTypeInfo<StructWithCustomConverterFactory> StructWithCustomConverterFactory { get; }
public JsonTypeInfo<ClassWithCustomConverterProperty> ClassWithCustomConverterProperty { get; }
public JsonTypeInfo<StructWithCustomConverterProperty> StructWithCustomConverterProperty { get; }
public JsonTypeInfo<ClassWithCustomConverterPropertyFactory> ClassWithCustomConverterPropertyFactory { get; }
public JsonTypeInfo<StructWithCustomConverterPropertyFactory> StructWithCustomConverterPropertyFactory { get; }
public JsonTypeInfo<ClassWithCustomConverterFactoryProperty> ClassWithCustomConverterFactoryProperty { get; }
public JsonTypeInfo<StructWithCustomConverterFactoryProperty> StructWithCustomConverterFactoryProperty { get; }
public JsonTypeInfo<ClassWithBadCustomConverter> ClassWithBadCustomConverter { get; }
public JsonTypeInfo<StructWithBadCustomConverter> StructWithBadCustomConverter { get; }
public JsonTypeInfo<PersonStruct?> NullablePersonStruct { get; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// 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 Microsoft.DotNet.RemoteExecutor;
Expand Down Expand Up @@ -89,5 +90,44 @@ internal record Person(string FirstName, string LastName);
internal partial class PersonJsonContext : JsonSerializerContext
{
}

// Regression test for https://github.com/dotnet/runtime/issues/62079
[Fact]
public static void SupportsPropertiesWithCustomConverterFactory()
{
var value = new ClassWithCustomConverterFactoryProperty { MyEnum = Serialization.Tests.SampleEnum.MinZero };
string json = JsonSerializer.Serialize(value, SingleClassWithCustomConverterFactoryPropertyContext.Default.ClassWithCustomConverterFactoryProperty);
Assert.Equal(@"{""MyEnum"":""MinZero""}", json);
}

public class ParentClass
{
public ClassWithCustomConverterFactoryProperty? Child { get; set; }
}

[JsonSerializable(typeof(ParentClass))]
internal partial class SingleClassWithCustomConverterFactoryPropertyContext : JsonSerializerContext
{
}

// Regression test for https://github.com/dotnet/runtime/issues/61860
[Fact]
public static void SupportsGenericParameterWithCustomConverterFactory()
{
var value = new List<TestEnum> { TestEnum.Cee };
string json = JsonSerializer.Serialize(value, GenericParameterWithCustomConverterFactoryContext.Default.ListTestEnum);
Assert.Equal(@"[""Cee""]", json);
}

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TestEnum
{
Aye, Bee, Cee
}

[JsonSerializable(typeof(List<TestEnum>))]
internal partial class GenericParameterWithCustomConverterFactoryContext : JsonSerializerContext
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(StructWithCustomConverterFactory))]
[JsonSerializable(typeof(ClassWithCustomConverterProperty))]
[JsonSerializable(typeof(StructWithCustomConverterProperty))]
[JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory))]
[JsonSerializable(typeof(StructWithCustomConverterPropertyFactory))]
[JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty))]
[JsonSerializable(typeof(StructWithCustomConverterFactoryProperty))]
[JsonSerializable(typeof(ClassWithBadCustomConverter))]
[JsonSerializable(typeof(StructWithBadCustomConverter))]
[JsonSerializable(typeof(PersonStruct?))]
Expand Down Expand Up @@ -82,8 +82,8 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterFactory);
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterProperty);
Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterProperty);
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterPropertyFactory);
Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterPropertyFactory);
Assert.NotNull(MetadataAndSerializationContext.Default.ClassWithCustomConverterFactoryProperty);
Assert.NotNull(MetadataAndSerializationContext.Default.StructWithCustomConverterFactoryProperty);
Assert.Throws<InvalidOperationException>(() => MetadataAndSerializationContext.Default.ClassWithBadCustomConverter);
Assert.Throws<InvalidOperationException>(() => MetadataAndSerializationContext.Default.StructWithBadCustomConverter);
Assert.Null(MetadataAndSerializationContext.Default.NullablePersonStruct.SerializeHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata)]
Expand Down Expand Up @@ -80,8 +80,8 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactory.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterProperty.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterProperty.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MetadataWithPerTypeAttributeContext.Default.ClassWithBadCustomConverter.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MetadataWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.NullablePersonStruct.SerializeHandler);
Expand Down Expand Up @@ -119,8 +119,8 @@ public override void EnsureFastPathGeneratedAsExpected()
[JsonSerializable(typeof(StructWithCustomConverterFactory))]
[JsonSerializable(typeof(ClassWithCustomConverterProperty))]
[JsonSerializable(typeof(StructWithCustomConverterProperty))]
[JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory))]
[JsonSerializable(typeof(StructWithCustomConverterPropertyFactory))]
[JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty))]
[JsonSerializable(typeof(StructWithCustomConverterFactoryProperty))]
[JsonSerializable(typeof(ClassWithBadCustomConverter))]
[JsonSerializable(typeof(StructWithBadCustomConverter))]
[JsonSerializable(typeof(PersonStruct?))]
Expand Down Expand Up @@ -187,8 +187,8 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MetadataContext.Default.StructWithCustomConverterFactory.SerializeHandler);
Assert.Null(MetadataContext.Default.ClassWithCustomConverterProperty.SerializeHandler);
Assert.Null(MetadataContext.Default.StructWithCustomConverterProperty.SerializeHandler);
Assert.Null(MetadataContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MetadataContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MetadataContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Null(MetadataContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MetadataContext.Default.ClassWithBadCustomConverter.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MetadataContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MetadataContext.Default.NullablePersonStruct.SerializeHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(StructWithCustomConverterFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(ClassWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(StructWithCustomConverterProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(ClassWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(StructWithCustomConverterPropertyFactory), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(ClassWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(StructWithCustomConverterFactoryProperty), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
Expand Down Expand Up @@ -82,8 +82,8 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactory.SerializeHandler);
Assert.Null(MixedModeContext.Default.ClassWithCustomConverterProperty.SerializeHandler);
Assert.Null(MixedModeContext.Default.StructWithCustomConverterProperty.SerializeHandler);
Assert.Null(MixedModeContext.Default.ClassWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MixedModeContext.Default.StructWithCustomConverterPropertyFactory.SerializeHandler);
Assert.Null(MixedModeContext.Default.ClassWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Null(MixedModeContext.Default.StructWithCustomConverterFactoryProperty.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MixedModeContext.Default.ClassWithBadCustomConverter.SerializeHandler);
Assert.Throws<InvalidOperationException>(() => MixedModeContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MixedModeContext.Default.NullablePersonStruct.SerializeHandler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,28 +246,28 @@ public virtual void RoundTripWithCustomPropertyConverterFactory_Class()
{
const string Json = "{\"MyEnum\":\"One\"}";

ClassWithCustomConverterPropertyFactory obj = new()
ClassWithCustomConverterFactoryProperty obj = new()
{
MyEnum = SampleEnum.One
};

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory));
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty));
}
else
{
string json = JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory);
string json = JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty);
Assert.Equal(Json, json);
}

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterPropertyFactory));
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.ClassWithCustomConverterFactoryProperty));
}
else
{
obj = JsonSerializer.Deserialize(Json, DefaultContext.ClassWithCustomConverterPropertyFactory);
obj = JsonSerializer.Deserialize(Json, DefaultContext.ClassWithCustomConverterFactoryProperty);
Assert.Equal(SampleEnum.One, obj.MyEnum);
}
}
Expand All @@ -277,28 +277,28 @@ public virtual void RoundTripWithCustomPropertyConverterFactory_Struct()
{
const string Json = "{\"MyEnum\":\"One\"}";

StructWithCustomConverterPropertyFactory obj = new()
StructWithCustomConverterFactoryProperty obj = new()
{
MyEnum = SampleEnum.One
};

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory));
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty));
}
else
{
string json = JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory);
string json = JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty);
Assert.Equal(Json, json);
}

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterPropertyFactory));
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(obj, DefaultContext.StructWithCustomConverterFactoryProperty));
}
else
{
obj = JsonSerializer.Deserialize(Json, DefaultContext.StructWithCustomConverterPropertyFactory);
obj = JsonSerializer.Deserialize(Json, DefaultContext.StructWithCustomConverterFactoryProperty);
Assert.Equal(SampleEnum.One, obj.MyEnum);
}
}
Expand Down
Loading