Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid name collision for covariant and hidden properties (#63443) #63823

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ private void CacheMember(
if (propGenSpec.DefaultIgnoreCondition == JsonIgnoreCondition.Always)
{
ignoredMembers ??= new Dictionary<string, PropertyGenerationSpec>();
ignoredMembers.Add(propGenSpec.ClrName, propGenSpec);
ignoredMembers.TryAdd(propGenSpec.ClrName, propGenSpec);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should really we be ignoring the result of TryAdd if it returns false? Is there a possibility that PropertyGenerationSpec varies significantly between the base and derived properties and the order of traversal can substantially impact how source code is being generated? cc @layomia

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If for example we only want to take the spec for the base property into account, we might want to introduce so that only the base member is inserted into the dictionary (using Add instead of TryAdd).

}
}

Expand Down
2 changes: 1 addition & 1 deletion src/libraries/System.Text.Json/gen/TypeGenerationSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public bool TryFilterSerializableProps(

if (propGenSpec.DefaultIgnoreCondition == JsonIgnoreCondition.Always)
{
(ignoredMembers ??= new Dictionary<string, PropertyGenerationSpec>()).Add(memberName, propGenSpec);
(ignoredMembers ??= new Dictionary<string, PropertyGenerationSpec>()).TryAdd(memberName, propGenSpec);
Kloizdena marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ private void CacheMember(JsonPropertyInfo jsonPropertyInfo, JsonPropertyDictiona

if (jsonPropertyInfo.IsIgnored)
{
(ignoredMembers ??= new Dictionary<string, JsonPropertyInfo>()).Add(memberName, jsonPropertyInfo);
(ignoredMembers ??= new Dictionary<string, JsonPropertyInfo>()).TryAdd(memberName, jsonPropertyInfo);
Kloizdena marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public interface ITestContext
public JsonTypeInfo<ClassWithBadCustomConverter> ClassWithBadCustomConverter { get; }
public JsonTypeInfo<StructWithBadCustomConverter> StructWithBadCustomConverter { get; }
public JsonTypeInfo<PersonStruct?> NullablePersonStruct { get; }
public JsonTypeInfo<CovariantDerived> CovariantDerived { get; }
public JsonTypeInfo<CovariantDerivedGeneric<string>> CovariantDerivedGenericString { get; }
public JsonTypeInfo<IgnoredPropertyBase> IgnoredPropertyBase { get; }
public JsonTypeInfo<NotIgnoredPropertyBase> NotIgnoredPropertyBase { get; }
public JsonTypeInfo<IgnoredPropertyBase_NotIgnoredPropertyDerived> IgnoredPropertyBase_NotIgnoredPropertyDerived { get; }
public JsonTypeInfo<IgnoredPropertyBase_IgnoredPropertyDerived> IgnoredPropertyBase_IgnoredPropertyDerived { get; }
public JsonTypeInfo<NotIgnoredPropertyBase_NotIgnoredPropertyDerived> NotIgnoredPropertyBase_NotIgnoredPropertyDerived { get; }
public JsonTypeInfo<NotIgnoredPropertyBase_IgnoredPropertyDerived> NotIgnoredPropertyBase_IgnoredPropertyDerived { get; }
}

internal partial class JsonContext : JsonSerializerContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(ClassWithBadCustomConverter))]
[JsonSerializable(typeof(StructWithBadCustomConverter))]
[JsonSerializable(typeof(PersonStruct?))]
[JsonSerializable(typeof(CovariantDerived))]
[JsonSerializable(typeof(CovariantDerivedGeneric<string>))]
[JsonSerializable(typeof(IgnoredPropertyBase))]
[JsonSerializable(typeof(NotIgnoredPropertyBase))]
[JsonSerializable(typeof(IgnoredPropertyBase_NotIgnoredPropertyDerived))]
[JsonSerializable(typeof(NotIgnoredPropertyBase_IgnoredPropertyDerived))]
[JsonSerializable(typeof(NotIgnoredPropertyBase_NotIgnoredPropertyDerived))]
[JsonSerializable(typeof(IgnoredPropertyBase_IgnoredPropertyDerived))]
internal partial class MetadataAndSerializationContext : JsonSerializerContext, ITestContext
{
public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Default;
Expand Down Expand Up @@ -88,6 +96,15 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Throws<InvalidOperationException>(() => MetadataAndSerializationContext.Default.StructWithBadCustomConverter);
Assert.Null(MetadataAndSerializationContext.Default.NullablePersonStruct.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.PersonStruct.SerializeHandler);
Assert.Null(MetadataAndSerializationContext.Default.CovariantDerived.SerializeHandler);
Assert.Null(MetadataAndSerializationContext.Default.CovariantDerivedGenericString.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.IgnoredPropertyBase.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.NotIgnoredPropertyBase.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.IgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.IgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.NotIgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MetadataAndSerializationContext.Default.NotIgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(CovariantDerived), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(CovariantDerivedGeneric<string>), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(IgnoredPropertyBase), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(NotIgnoredPropertyBase), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(IgnoredPropertyBase_NotIgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(NotIgnoredPropertyBase_IgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(NotIgnoredPropertyBase_NotIgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(IgnoredPropertyBase_IgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata)]
internal partial class MetadataWithPerTypeAttributeContext : JsonSerializerContext, ITestContext
{
public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata;
Expand Down Expand Up @@ -86,6 +94,14 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Throws<InvalidOperationException>(() => MetadataWithPerTypeAttributeContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.NullablePersonStruct.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.PersonStruct.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.CovariantDerived.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.CovariantDerivedGenericString.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.IgnoredPropertyBase.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.NotIgnoredPropertyBase.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.IgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.IgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.NotIgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataWithPerTypeAttributeContext.Default.NotIgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
}
}

Expand Down Expand Up @@ -124,6 +140,14 @@ public override void EnsureFastPathGeneratedAsExpected()
[JsonSerializable(typeof(ClassWithBadCustomConverter))]
[JsonSerializable(typeof(StructWithBadCustomConverter))]
[JsonSerializable(typeof(PersonStruct?))]
[JsonSerializable(typeof(CovariantDerived))]
[JsonSerializable(typeof(CovariantDerivedGeneric<string>))]
[JsonSerializable(typeof(IgnoredPropertyBase))]
[JsonSerializable(typeof(NotIgnoredPropertyBase))]
[JsonSerializable(typeof(IgnoredPropertyBase_NotIgnoredPropertyDerived))]
[JsonSerializable(typeof(NotIgnoredPropertyBase_IgnoredPropertyDerived))]
[JsonSerializable(typeof(NotIgnoredPropertyBase_NotIgnoredPropertyDerived))]
[JsonSerializable(typeof(IgnoredPropertyBase_IgnoredPropertyDerived))]
internal partial class MetadataContext : JsonSerializerContext, ITestContext
{
public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata;
Expand Down Expand Up @@ -193,6 +217,14 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Throws<InvalidOperationException>(() => MetadataContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MetadataContext.Default.NullablePersonStruct.SerializeHandler);
Assert.Null(MetadataContext.Default.PersonStruct.SerializeHandler);
Assert.Null(MetadataContext.Default.CovariantDerived.SerializeHandler);
Assert.Null(MetadataContext.Default.CovariantDerivedGenericString.SerializeHandler);
Assert.Null(MetadataContext.Default.IgnoredPropertyBase.SerializeHandler);
Assert.Null(MetadataContext.Default.NotIgnoredPropertyBase.SerializeHandler);
Assert.Null(MetadataContext.Default.IgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataContext.Default.IgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataContext.Default.NotIgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.Null(MetadataContext.Default.NotIgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ namespace System.Text.Json.SourceGeneration.Tests
[JsonSerializable(typeof(ClassWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(StructWithBadCustomConverter), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(PersonStruct?), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(CovariantDerived), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(CovariantDerivedGeneric<string>), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(IgnoredPropertyBase), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(NotIgnoredPropertyBase), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(IgnoredPropertyBase_NotIgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(NotIgnoredPropertyBase_IgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(NotIgnoredPropertyBase_NotIgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(IgnoredPropertyBase_IgnoredPropertyDerived), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
internal partial class MixedModeContext : JsonSerializerContext, ITestContext
{
public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization;
Expand Down Expand Up @@ -88,6 +96,14 @@ public override void EnsureFastPathGeneratedAsExpected()
Assert.Throws<InvalidOperationException>(() => MixedModeContext.Default.StructWithBadCustomConverter.SerializeHandler);
Assert.Null(MixedModeContext.Default.NullablePersonStruct.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.PersonStruct.SerializeHandler);
Assert.Null(MixedModeContext.Default.CovariantDerived.SerializeHandler);
Assert.Null(MixedModeContext.Default.CovariantDerivedGenericString.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.IgnoredPropertyBase.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.NotIgnoredPropertyBase.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.IgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.IgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.NotIgnoredPropertyBase_NotIgnoredPropertyDerived.SerializeHandler);
Assert.NotNull(MixedModeContext.Default.NotIgnoredPropertyBase_IgnoredPropertyDerived.SerializeHandler);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -882,5 +882,121 @@ public virtual void NullableStruct()
Assert.Equal("Jane", person.Value.FirstName);
Assert.Equal("Doe", person.Value.LastName);
}

[Fact]
public void JsonIgnoreForCovariantProperties()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell just by looking at the diff, but we should make sure that these tests are exercised both by the fast-path and metadata-based source generator.

{
var derived = new CovariantDerived();

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(derived, DefaultContext.CovariantDerived));
}
else
{
string json = JsonSerializer.Serialize(derived, DefaultContext.CovariantDerived);
JsonTestHelper.AssertJsonEqual(@"{}", json);
derived = JsonSerializer.Deserialize(json, DefaultContext.CovariantDerived);
Assert.Null(derived.Id);
}
}

[Fact]
public void JsonIgnoreForCovariantGenericProperties()
{
var derived = new CovariantDerivedGeneric<string>();
if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(derived, DefaultContext.CovariantDerivedGenericString));
}
else
{
string json = JsonSerializer.Serialize(derived, DefaultContext.CovariantDerivedGenericString);
JsonTestHelper.AssertJsonEqual(@"{}", json);

derived = JsonSerializer.Deserialize(json, DefaultContext.CovariantDerivedGenericString);
Assert.Null(derived.Id);
}
}

[Fact]
public void JsonIgnoreForHiddenProperties_IgnoredBase_NotIgnoredDerived()
{
var derived = new IgnoredPropertyBase_NotIgnoredPropertyDerived { Id = "Test" };

string json = JsonSerializer.Serialize(derived, DefaultContext.IgnoredPropertyBase_NotIgnoredPropertyDerived);
JsonTestHelper.AssertJsonEqual("{\"Id\":\"Test\"}", json);

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
// Deserialization not supported in fast path serialization only mode
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize(json, DefaultContext.IgnoredPropertyBase_NotIgnoredPropertyDerived));
}
else
{
derived = JsonSerializer.Deserialize(json, DefaultContext.IgnoredPropertyBase_NotIgnoredPropertyDerived);
Assert.Equal("Test", derived.Id);
}
}

[Fact]
public void JsonIgnoreForHiddenProperties_NotIgnoredBase_IgnoredDerived()
{
var derived = new NotIgnoredPropertyBase_IgnoredPropertyDerived { Id = "Test" };

string json = JsonSerializer.Serialize(derived, DefaultContext.NotIgnoredPropertyBase_IgnoredPropertyDerived);
JsonTestHelper.AssertJsonEqual("{\"Id\":null}", json);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this should be expected, considering that for JsonIgnoreForHiddenProperties_NotIgnoredBase_NotIgnoredDerived we're not showing both Ids my gut tells me I should not be seeing any Ids here either. We should be consistent for hidden non-ignored properties I think. cc: @layomia @eiriktsarpalis @steveharter

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure either, what are the semantics in the reflection-based for that scenario?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried it out without JsonSerializerContext with the following code:

using System.Text.Json;
using System.Text.Json.Serialization;

NotIgnoredBase b1 = new NotIgnoredBase();
NotIgnoredBase_NotIgnoredDerived b2 = new NotIgnoredBase_NotIgnoredDerived();
NotIgnoredBase b3 = new NotIgnoredBase_NotIgnoredDerived();
NotIgnoredBase_IgnoredDerived b4 = new NotIgnoredBase_IgnoredDerived();
NotIgnoredBase b5 = new NotIgnoredBase_IgnoredDerived();
b1.Property = "1"; // Property on base
b2.Property = "2";
b3.Property = "3"; // Property on base
b4.Property = "4";
b5.Property = "5"; // Property on base

Console.WriteLine(JsonSerializer.Serialize(b1));
Console.WriteLine(JsonSerializer.Serialize(b2));
Console.WriteLine(JsonSerializer.Serialize(b3));
Console.WriteLine(JsonSerializer.Serialize(b4));
Console.WriteLine(JsonSerializer.Serialize(b5));

Console.WriteLine();

Console.WriteLine(JsonSerializer.Serialize(b1, typeof(NotIgnoredBase)));
Console.WriteLine(JsonSerializer.Serialize(b2, typeof(NotIgnoredBase)));
Console.WriteLine(JsonSerializer.Serialize(b2, typeof(NotIgnoredBase_NotIgnoredDerived)));
Console.WriteLine(JsonSerializer.Serialize(b3, typeof(NotIgnoredBase)));
Console.WriteLine(JsonSerializer.Serialize(b4, typeof(NotIgnoredBase)));
Console.WriteLine(JsonSerializer.Serialize(b4, typeof(NotIgnoredBase_IgnoredDerived)));
Console.WriteLine(JsonSerializer.Serialize(b5, typeof(NotIgnoredBase)));

public class NotIgnoredBase
{
	public string Property { get; set; } = nameof(NotIgnoredBase);
}
public class NotIgnoredBase_NotIgnoredDerived : NotIgnoredBase
{
	public new string Property { get; set; } = nameof(NotIgnoredBase_NotIgnoredDerived);
}
public class NotIgnoredBase_IgnoredDerived : NotIgnoredBase
{
	[JsonIgnore]
	public new string Property { get; set; } = nameof(NotIgnoredBase_IgnoredDerived);
}

Results:

{"Property":"1"}
{"Property":"2"}
{"Property":"3"}
{"Property":"NotIgnoredBase"}
{"Property":"5"}

{"Property":"1"}
{"Property":"NotIgnoredBase"}
{"Property":"2"}
{"Property":"3"}
{"Property":"NotIgnoredBase"}
{"Property":"NotIgnoredBase"}
{"Property":"5"}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turns out the reflection-based serializer also throws an exception if the properties are ignored both on the base and the derived class.

	IgnoredBase_IgnoredDerived b4 = new IgnoredBase_IgnoredDerived();
	Console.WriteLine(JsonSerializer.Serialize(b4));


public class IgnoredBase
{
	[JsonIgnore]
	public string Property { get; set; } = nameof(IgnoredBase);
}
public class IgnoredBase_IgnoredDerived : IgnoredBase
{
	[JsonIgnore]
	public new string Property { get; set; } = nameof(IgnoredBase_IgnoredDerived);
}

Exception:

System.ArgumentException: An item with the same key has already been added. Key: Property
   at System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException[T](T key)
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.CacheMember(JsonPropertyInfo jsonPropertyInfo, JsonPropertyDictionary`1 propertyCache, Dictionary`2& ignoredMembers) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs:line 409
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo.CacheMember(Type declaringType, Type memberType, MemberInfo memberInfo, Boolean isVirtual, Nullable`1 typeNumberHandling, Boolean& propertyOrderSpecified, Dictionary`2& ignoredMembers) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs:line 373
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo..ctor(Type type, JsonConverter converter, Type runtimeType, JsonSerializerOptions options) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs:line 224
   at System.Text.Json.JsonSerializerOptions.<InitializeForReflectionSerializer>g__CreateJsonTypeInfo|112_0(Type type, JsonSerializerOptions options) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs:line 596
   at System.Text.Json.JsonSerializerOptions.GetClassFromContextOrCreate(Type type) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs:line 625
   at System.Text.Json.JsonSerializerOptions.GetOrAddClass(Type type) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs:line 605
   at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type runtimeType) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Helpers.cs:line 26
   at System.Text.Json.JsonSerializer.Serialize(Object value, Type inputType, JsonSerializerOptions options) in /_/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializer.Write.String.cs:line 63
   at Program.<Main>$(String[] args) in C:\src\JsonTest\JsonTest\Program.cs:line 54

This error would be also fixed by the PR:
https://github.com/dotnet/runtime/pull/63823/files#diff-8d841bf86f2a9da83758276bd17f55d002ee5b55f58b72da658df351a7c22bc0L412-R412

Copy link
Contributor Author

@Kloizdena Kloizdena Jan 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full code to compare the source generator and reflection-based output:

using System;
using System.Text.Json;
using System.Text.Json.Serialization;
{
	NotIgnoredBase b1 = new NotIgnoredBase();
	NotIgnoredBase_NotIgnoredDerived b2 = new NotIgnoredBase_NotIgnoredDerived();
	NotIgnoredBase b3 = new NotIgnoredBase_NotIgnoredDerived();
	NotIgnoredBase_IgnoredDerived b4 = new NotIgnoredBase_IgnoredDerived();
	NotIgnoredBase b5 = new NotIgnoredBase_IgnoredDerived();
	b1.Property = "1"; // Property on base
	b2.Property = "2";
	b3.Property = "3"; // Property on base
	b4.Property = "4";
	b5.Property = "5"; // Property on base

	Console.WriteLine("NotIgnoredBase, Reflection-based:");
	Console.WriteLine(JsonSerializer.Serialize(b1, typeof(NotIgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b2, typeof(NotIgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b2, typeof(NotIgnoredBase_NotIgnoredDerived)));
	Console.WriteLine(JsonSerializer.Serialize(b3, typeof(NotIgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b4, typeof(NotIgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b4, typeof(NotIgnoredBase_IgnoredDerived)));
	Console.WriteLine(JsonSerializer.Serialize(b5, typeof(NotIgnoredBase)));

	Console.WriteLine();
	Console.WriteLine("NotIgnoredBase, Source generator-based:");
	Console.WriteLine(JsonSerializer.Serialize(b1, JsonContext.Default.NotIgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b2, JsonContext.Default.NotIgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b2, JsonContext.Default.NotIgnoredBase_NotIgnoredDerived));
	Console.WriteLine(JsonSerializer.Serialize(b3, JsonContext.Default.NotIgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b4, JsonContext.Default.NotIgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b4, JsonContext.Default.NotIgnoredBase_IgnoredDerived));
	Console.WriteLine(JsonSerializer.Serialize(b5, JsonContext.Default.NotIgnoredBase));

}
{
	IgnoredBase b1 = new IgnoredBase();
	IgnoredBase_NotIgnoredDerived b2 = new IgnoredBase_NotIgnoredDerived();
	IgnoredBase b3 = new IgnoredBase_NotIgnoredDerived();
	IgnoredBase_IgnoredDerived b4 = new IgnoredBase_IgnoredDerived();
	IgnoredBase b5 = new IgnoredBase_IgnoredDerived();
	b1.Property = "1"; // Property on base
	b2.Property = "2";
	b3.Property = "3"; // Property on base
	b4.Property = "4";
	b5.Property = "5"; // Property on base
	Console.WriteLine();
	Console.WriteLine("IgnoredBase, Reflection-based:");
	Console.WriteLine(JsonSerializer.Serialize(b1, typeof(IgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b2, typeof(IgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b2, typeof(IgnoredBase_NotIgnoredDerived)));
	Console.WriteLine(JsonSerializer.Serialize(b3, typeof(IgnoredBase)));
	Console.WriteLine(JsonSerializer.Serialize(b4, typeof(IgnoredBase)));
	Console.WriteLine("Serialization error"/*JsonSerializer.Serialize(b4, typeof(IgnoredBase_IgnoredDerived))*/);
	Console.WriteLine(JsonSerializer.Serialize(b5, typeof(IgnoredBase)));

	Console.WriteLine();
	Console.WriteLine("IgnoredBase, Source generator-based:");
	Console.WriteLine(JsonSerializer.Serialize(b1, JsonContext.Default.IgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b2, JsonContext.Default.IgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b2, JsonContext.Default.IgnoredBase_NotIgnoredDerived));
	Console.WriteLine(JsonSerializer.Serialize(b3, JsonContext.Default.IgnoredBase));
	Console.WriteLine(JsonSerializer.Serialize(b4, JsonContext.Default.IgnoredBase));
	Console.WriteLine("Source generator error"/*JsonSerializer.Serialize(b4, JsonContext.Default.IgnoredBase_IgnoredDerived)*/);
	Console.WriteLine(JsonSerializer.Serialize(b5, JsonContext.Default.IgnoredBase));

	Console.WriteLine();
}

public class NotIgnoredBase
{
	public string Property { get; set; } = nameof(NotIgnoredBase);
}
public class NotIgnoredBase_NotIgnoredDerived : NotIgnoredBase
{
	public new string Property { get; set; } = nameof(NotIgnoredBase_NotIgnoredDerived);
}
public class NotIgnoredBase_IgnoredDerived : NotIgnoredBase
{
	[JsonIgnore]
	public new string Property { get; set; } = nameof(NotIgnoredBase_IgnoredDerived);
}
public class IgnoredBase
{
	[JsonIgnore]
	public string Property { get; set; } = nameof(IgnoredBase);
}
public class IgnoredBase_NotIgnoredDerived : IgnoredBase
{
	public new string Property { get; set; } = nameof(IgnoredBase_NotIgnoredDerived);
}
public class IgnoredBase_IgnoredDerived : IgnoredBase
{
	[JsonIgnore]
	public new string Property { get; set; } = nameof(IgnoredBase_IgnoredDerived);
}

[JsonSerializable(typeof(NotIgnoredBase))]
[JsonSerializable(typeof(NotIgnoredBase_NotIgnoredDerived))]
[JsonSerializable(typeof(NotIgnoredBase_IgnoredDerived))]
[JsonSerializable(typeof(IgnoredBase))]
[JsonSerializable(typeof(IgnoredBase_NotIgnoredDerived))]
//[JsonSerializable(typeof(IgnoredBase_IgnoredDerived))]
public partial class JsonContext : JsonSerializerContext
{ }

Output:

NotIgnoredBase, Reflection-based:
{"Property":"1"}
{"Property":"NotIgnoredBase"}
{"Property":"2"}
{"Property":"3"}
{"Property":"NotIgnoredBase"}
{"Property":"NotIgnoredBase"}
{"Property":"5"}

NotIgnoredBase, Source generator-based:
{"Property":"1"}
{"Property":"NotIgnoredBase"}
{"Property":"2"}
{"Property":"3"}
{"Property":"NotIgnoredBase"}
{"Property":"NotIgnoredBase"}
{"Property":"5"}

IgnoredBase, Reflection-based:
{}
{}
{"Property":"2"}
{}
{}
Serialization error
{}

IgnoredBase, Source generator-based:
{}
{}
{"Property":"2"}
{}
{}
Source generator error
{}


if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
// Deserialization not supported in fast path serialization only mode
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize(json, DefaultContext.NotIgnoredPropertyBase_IgnoredPropertyDerived));
}
else
{
derived = JsonSerializer.Deserialize(json, DefaultContext.NotIgnoredPropertyBase_IgnoredPropertyDerived);
Assert.Null(derived.Id);
}
}

[Fact]
public void JsonIgnoreForHiddenProperties_IgnoredBase_IgnoredDerived()
{
var derived = new IgnoredPropertyBase_IgnoredPropertyDerived { Id = "Test" };

string json = JsonSerializer.Serialize(derived, DefaultContext.IgnoredPropertyBase_IgnoredPropertyDerived);
JsonTestHelper.AssertJsonEqual(@"{}", json);

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
// Deserialization not supported in fast path serialization only mode
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize(json, DefaultContext.IgnoredPropertyBase_IgnoredPropertyDerived));
}
else
{
derived = JsonSerializer.Deserialize(json, DefaultContext.IgnoredPropertyBase_IgnoredPropertyDerived);
Assert.Null(derived.Id);
}
}

[Fact]
public void JsonIgnoreForHiddenProperties_NotIgnoredBase_NotIgnoredDerived()
{
var derived = new NotIgnoredPropertyBase_NotIgnoredPropertyDerived { Id = "Test" };

string json = JsonSerializer.Serialize(derived, DefaultContext.NotIgnoredPropertyBase_NotIgnoredPropertyDerived);
JsonTestHelper.AssertJsonEqual("{\"Id\":\"Test\"}", json);

if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
{
// Deserialization not supported in fast path serialization only mode
Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize(json, DefaultContext.NotIgnoredPropertyBase_NotIgnoredPropertyDerived));
}
else
{
derived = JsonSerializer.Deserialize(json, DefaultContext.NotIgnoredPropertyBase_NotIgnoredPropertyDerived);
Assert.Equal("Test", derived.Id);
}
}
}
}
Loading