1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-09 17:44:48 +09:00

[release/9.0-staging] Fix case-insensitive JSON deserialization of enum member names (#112057)

* Fix case-insensitive deserialization of default enum values

* renaming

* Update comment

---------

Co-authored-by: Pranav Senthilnathan <pranas@microsoft.com>
This commit is contained in:
github-actions[bot] 2025-02-18 16:44:07 -08:00 committed by GitHub
parent e08ac0ec15
commit 33a063585d
Signed by: github
GPG key ID: B5690EEEBB952194
2 changed files with 74 additions and 3 deletions

View file

@ -601,9 +601,9 @@ namespace System.Text.Json.Serialization.Converters
{
Debug.Assert(JsonName.Equals(other.JsonName, StringComparison.OrdinalIgnoreCase), "The conflicting entry must be equal up to case insensitivity.");
if (Kind is EnumFieldNameKind.Default || JsonName.Equals(other.JsonName, StringComparison.Ordinal))
if (ConflictsWith(this, other))
{
// Silently discard if the preceding entry is the default or has identical name.
// Silently discard if the new entry conflicts with the preceding entry
return;
}
@ -612,13 +612,34 @@ namespace System.Text.Json.Serialization.Converters
// Walk the existing list to ensure we do not add duplicates.
foreach (EnumFieldInfo conflictingField in conflictingFields)
{
if (conflictingField.Kind is EnumFieldNameKind.Default || conflictingField.JsonName.Equals(other.JsonName, StringComparison.Ordinal))
if (ConflictsWith(conflictingField, other))
{
return;
}
}
conflictingFields.Add(other);
// Determines whether the first field info matches everything that the second field info matches,
// in which case the second field info is redundant and doesn't need to be added to the list.
static bool ConflictsWith(EnumFieldInfo current, EnumFieldInfo other)
{
// The default name matches everything case-insensitively.
if (current.Kind is EnumFieldNameKind.Default)
{
return true;
}
// current matches case-sensitively since it's not the default name.
// other matches case-insensitively, so it matches more than current.
if (other.Kind is EnumFieldNameKind.Default)
{
return false;
}
// Both are case-sensitive so they need to be identical.
return current.JsonName.Equals(other.JsonName, StringComparison.Ordinal);
}
}
public EnumFieldInfo? GetMatchingField(ReadOnlySpan<char> input)

View file

@ -1207,5 +1207,55 @@ namespace System.Text.Json.Serialization.Tests
[JsonStringEnumMemberName("Comma separators not allowed, in flags enums")]
Value
}
[Theory]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.SnakeCaseLower)]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.KebabCaseUpper)]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.KebabCaseLower)]
[InlineData("\"pAsCaLcAsE\"", EnumWithVaryingNamingPolicies.PascalCase, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"pAsCaLcAsE\"", EnumWithVaryingNamingPolicies.PascalCase, JsonKnownNamingPolicy.SnakeCaseLower)]
[InlineData("\"pAsCaLcAsE\"", EnumWithVaryingNamingPolicies.PascalCase, JsonKnownNamingPolicy.KebabCaseUpper)]
[InlineData("\"pAsCaLcAsE\"", EnumWithVaryingNamingPolicies.PascalCase, JsonKnownNamingPolicy.KebabCaseLower)]
[InlineData("\"sNaKe_CaSe_UpPeR\"", EnumWithVaryingNamingPolicies.SNAKE_CASE_UPPER, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"sNaKe_CaSe_LoWeR\"", EnumWithVaryingNamingPolicies.snake_case_lower, JsonKnownNamingPolicy.SnakeCaseLower)]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.CamelCase)]
[InlineData("\"a\"", EnumWithVaryingNamingPolicies.A, JsonKnownNamingPolicy.CamelCase)]
[InlineData("\"a\"", EnumWithVaryingNamingPolicies.A, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"a\"", EnumWithVaryingNamingPolicies.A, JsonKnownNamingPolicy.SnakeCaseLower)]
[InlineData("\"a\"", EnumWithVaryingNamingPolicies.A, JsonKnownNamingPolicy.KebabCaseUpper)]
[InlineData("\"a\"", EnumWithVaryingNamingPolicies.A, JsonKnownNamingPolicy.KebabCaseLower)]
[InlineData("\"B\"", EnumWithVaryingNamingPolicies.b, JsonKnownNamingPolicy.CamelCase)]
[InlineData("\"B\"", EnumWithVaryingNamingPolicies.b, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"B\"", EnumWithVaryingNamingPolicies.b, JsonKnownNamingPolicy.SnakeCaseLower)]
[InlineData("\"B\"", EnumWithVaryingNamingPolicies.b, JsonKnownNamingPolicy.KebabCaseUpper)]
[InlineData("\"B\"", EnumWithVaryingNamingPolicies.b, JsonKnownNamingPolicy.KebabCaseLower)]
public static void StringConverterWithNamingPolicyIsCaseInsensitive(string json, EnumWithVaryingNamingPolicies expectedValue, JsonKnownNamingPolicy namingPolicy)
{
JsonNamingPolicy policy = namingPolicy switch
{
JsonKnownNamingPolicy.CamelCase => JsonNamingPolicy.CamelCase,
JsonKnownNamingPolicy.SnakeCaseLower => JsonNamingPolicy.SnakeCaseLower,
JsonKnownNamingPolicy.SnakeCaseUpper => JsonNamingPolicy.SnakeCaseUpper,
JsonKnownNamingPolicy.KebabCaseLower => JsonNamingPolicy.KebabCaseLower,
JsonKnownNamingPolicy.KebabCaseUpper => JsonNamingPolicy.KebabCaseUpper,
_ => throw new ArgumentOutOfRangeException(nameof(namingPolicy)),
};
JsonSerializerOptions options = new() { Converters = { new JsonStringEnumConverter(policy) } };
EnumWithVaryingNamingPolicies value = JsonSerializer.Deserialize<EnumWithVaryingNamingPolicies>(json, options);
Assert.Equal(expectedValue, value);
}
public enum EnumWithVaryingNamingPolicies
{
SNAKE_CASE_UPPER,
snake_case_lower,
camelCase,
PascalCase,
A,
b,
}
}
}