Skip to content

Commit 59edaad

Browse files
github-actions[bot]eiriktsarpalisjeffhandley
authored
[release/8.0] Honor JsonSerializerOptions.PropertyNameCaseInsensitive in property name conflict resolution. (#93935)
* Honor JsonSerializerOptions.PropertyNameCaseInsensitive in property name conflict resolution. * Update src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs Co-authored-by: Jeff Handley <[email protected]> * Address feedback --------- Co-authored-by: Eirik Tsarpalis <[email protected]> Co-authored-by: Jeff Handley <[email protected]>
1 parent 488a8a3 commit 59edaad

File tree

6 files changed

+38
-9
lines changed

6 files changed

+38
-9
lines changed

src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs

+3-4
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ private List<PropertyGenerationSpec> ParsePropertyGenerationSpecs(
863863
{
864864
Location? typeLocation = typeToGenerate.Location;
865865
List<PropertyGenerationSpec> properties = new();
866-
PropertyHierarchyResolutionState state = new();
866+
PropertyHierarchyResolutionState state = new(options);
867867
hasExtensionDataProperty = false;
868868

869869
// Walk the type hierarchy starting from the current type up to the base type(s)
@@ -970,11 +970,10 @@ bool PropertyIsOverriddenAndIgnored(IPropertySymbol property, Dictionary<string,
970970
}
971971
}
972972

973-
private ref struct PropertyHierarchyResolutionState
973+
private ref struct PropertyHierarchyResolutionState(SourceGenerationOptionsSpec? options)
974974
{
975-
public PropertyHierarchyResolutionState() { }
976975
public readonly List<int> Properties = new();
977-
public Dictionary<string, (PropertyGenerationSpec, ISymbol, int index)> AddedProperties = new();
976+
public Dictionary<string, (PropertyGenerationSpec, ISymbol, int index)> AddedProperties = new(options?.PropertyNameCaseInsensitive == true ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);
978977
public Dictionary<string, ISymbol>? IgnoredMembers;
979978
public bool IsPropertyOrderSpecified;
980979
public bool HasInvalidConfigurationForFastPath;

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private static void PopulateProperties(JsonTypeInfo typeInfo)
8888
bool constructorHasSetsRequiredMembersAttribute =
8989
typeInfo.Converter.ConstructorInfo?.HasSetsRequiredMembersAttribute() ?? false;
9090

91-
JsonTypeInfo.PropertyHierarchyResolutionState state = new();
91+
JsonTypeInfo.PropertyHierarchyResolutionState state = new(typeInfo.Options);
9292

9393
// Walk the type hierarchy starting from the current type up to the base type(s)
9494
foreach (Type currentType in typeInfo.Type.GetSortedTypeHierarchy())

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal static void PopulateProperties(JsonTypeInfo typeInfo, JsonTypeInfo.Json
137137

138138
// Regardless of the source generator we need to re-run the naming conflict resolution algorithm
139139
// at run time since it is possible that the naming policy or other configs can be different then.
140-
JsonTypeInfo.PropertyHierarchyResolutionState state = new();
140+
JsonTypeInfo.PropertyHierarchyResolutionState state = new(typeInfo.Options);
141141

142142
foreach (JsonPropertyInfo jsonPropertyInfo in properties)
143143
{

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs

+2-3
Original file line numberDiff line numberDiff line change
@@ -992,10 +992,9 @@ public JsonPropertyInfo CreateJsonPropertyInfo(Type propertyType, string name)
992992
internal abstract ValueTask<object?> DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken);
993993
internal abstract object? DeserializeAsObject(Stream utf8Json);
994994

995-
internal ref struct PropertyHierarchyResolutionState
995+
internal ref struct PropertyHierarchyResolutionState(JsonSerializerOptions options)
996996
{
997-
public PropertyHierarchyResolutionState() { }
998-
public Dictionary<string, (JsonPropertyInfo, int index)> AddedProperties = new();
997+
public Dictionary<string, (JsonPropertyInfo, int index)> AddedProperties = new(options.PropertyNameCaseInsensitive ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);
999998
public Dictionary<string, JsonPropertyInfo>? IgnoredProperties;
1000999
public bool IsPropertyOrderSpecified;
10011000
}

src/libraries/System.Text.Json/tests/Common/PropertyNameTests.cs

+29
Original file line numberDiff line numberDiff line change
@@ -494,5 +494,34 @@ public class ClassWithSpecialCharacters
494494
[JsonPropertyName("\uA000_2")] // Valid C# property name: \uA000_2
495495
public int YiIt_2 { get; set; }
496496
}
497+
498+
[Theory]
499+
[InlineData(false)]
500+
[InlineData(true)]
501+
public async Task ClassWithIgnoredCaseInsensitiveConflict_RespectsIgnoredMember(bool propertyNameCaseInsensitive)
502+
{
503+
// Regression test for https://github.com/dotnet/runtime/issues/93903
504+
// specifically for propertyNameCaseInsensitive := true
505+
506+
JsonSerializerOptions options = Serializer.CreateOptions(makeReadOnly: false);
507+
options.PropertyNameCaseInsensitive = propertyNameCaseInsensitive;
508+
509+
var value = new ClassWithIgnoredCaseInsensitiveConflict { name = "lowercase", Name = "uppercase" };
510+
string json = await Serializer.SerializeWrapper(value, options);
511+
512+
Assert.Equal("""{"name":"lowercase"}""", json);
513+
514+
value = await Serializer.DeserializeWrapper<ClassWithIgnoredCaseInsensitiveConflict>(json, options);
515+
Assert.Equal("lowercase", value.name);
516+
Assert.Null(value.Name);
517+
}
518+
519+
public class ClassWithIgnoredCaseInsensitiveConflict
520+
{
521+
public string name { get; set; }
522+
523+
[JsonIgnore]
524+
public string Name { get; set; }
525+
}
497526
}
498527
}

src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyNameTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public PropertyNameTests_Metadata()
2828
[JsonSerializable(typeof(ObjectPropertyNamesDifferentByCaseOnly_TestClass))]
2929
[JsonSerializable(typeof(OverridePropertyNameDesignTime_TestClass))]
3030
[JsonSerializable(typeof(SimpleTestClass))]
31+
[JsonSerializable(typeof(ClassWithIgnoredCaseInsensitiveConflict))]
3132
internal sealed partial class PropertyNameTestsContext_Metadata : JsonSerializerContext
3233
{
3334
}
@@ -53,6 +54,7 @@ public PropertyNameTests_Default()
5354
[JsonSerializable(typeof(ObjectPropertyNamesDifferentByCaseOnly_TestClass))]
5455
[JsonSerializable(typeof(OverridePropertyNameDesignTime_TestClass))]
5556
[JsonSerializable(typeof(SimpleTestClass))]
57+
[JsonSerializable(typeof(ClassWithIgnoredCaseInsensitiveConflict))]
5658
internal sealed partial class PropertyNameTestsContext_Default : JsonSerializerContext
5759
{
5860
}

0 commit comments

Comments
 (0)