diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx
index b67fc02b7a5a86..8b259b4fa14212 100644
--- a/src/libraries/System.Text.Json/src/Resources/Strings.resx
+++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx
@@ -232,7 +232,7 @@
'{0}' is invalid within a JSON string. The string should be correctly escaped.
- Enum type '{0}' uses unsupported identifier '{1}'. It must not be null, empty, or containing leading or trailing whitespace. Flags enums must additionally not contain commas.
+ Enum type '{0}' uses unsupported identifier '{1}'. It must not be null nor contain leading or trailing whitespace. Flags enums must additionally not be empty nor contain commas.
'{0}' is an invalid token type for the end of the JSON payload. Expected either 'EndArray' or 'EndObject'.
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
index ebc89780decd41..0d2d7a70bb4aec 100644
--- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
+++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/EnumConverter.cs
@@ -304,9 +304,26 @@ private bool TryParseNamedEnum(
{
#if NET
Dictionary.AlternateLookup> lookup = _enumFieldInfoIndex.GetAlternateLookup>();
- ReadOnlySpan rest = source;
#else
Dictionary lookup = _enumFieldInfoIndex;
+#endif
+
+ if (!s_isFlagsEnum)
+ {
+ if (lookup.TryGetValue(source, out EnumFieldInfo? firstResult) &&
+ firstResult.GetMatchingField(source) is EnumFieldInfo match)
+ {
+ result = ConvertFromUInt64(match.Key);
+ return true;
+ }
+
+ result = default;
+ return false;
+ }
+
+#if NET
+ ReadOnlySpan rest = source;
+#else
ReadOnlySpan rest = source.AsSpan();
#endif
ulong key = 0;
@@ -572,11 +589,13 @@ private static string ResolveAndValidateJsonName(string name, JsonNamingPolicy?
name = namingPolicy.ConvertName(name);
}
- if (string.IsNullOrEmpty(name) || char.IsWhiteSpace(name[0]) || char.IsWhiteSpace(name[name.Length - 1]) ||
- (s_isFlagsEnum && name.Contains(',')))
+ if (name is null ||
+ (name.Length > 0 && (char.IsWhiteSpace(name[0]) || char.IsWhiteSpace(name[name.Length - 1]))) ||
+ (s_isFlagsEnum && (name.Length == 0 || name.Contains(','))))
{
- // Reject null or empty strings or strings with leading or trailing whitespace.
- // In the case of flags additionally reject strings containing commas.
+ // Reject null strings or strings with leading or trailing whitespace.
+ // In the case of flags additionally reject empty strings or strings containing commas,
+ // both of which would introduce ambiguity in flag value parsing and formatting.
ThrowHelper.ThrowInvalidOperationException_UnsupportedEnumIdentifier(typeof(T), name);
}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs
index ada90fab227649..12e6c557ceb66e 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/EnumConverterTests.cs
@@ -1259,6 +1259,7 @@ public enum EnumWithInvalidMemberName1
Value
}
+ [Flags]
public enum EnumWithInvalidMemberName2
{
[JsonStringEnumMemberName("")]
@@ -1290,6 +1291,65 @@ public enum EnumWithInvalidMemberName6
Value
}
+ [Theory]
+ [InlineData(YesOrNoOrEmpty.Yes, "\"y\"")]
+ [InlineData(YesOrNoOrEmpty.No, "\"n\"")]
+ [InlineData(YesOrNoOrEmpty.Empty, "\"\"")]
+ public static void EnumWithEmptyStringMemberName_NonFlags_RoundtripsAsExpected(YesOrNoOrEmpty value, string expectedJson)
+ {
+ string json = JsonSerializer.Serialize(value, s_optionsWithStringEnumConverter);
+ Assert.Equal(expectedJson, json);
+ Assert.Equal(value, JsonSerializer.Deserialize(json, s_optionsWithStringEnumConverter));
+ }
+
+ [Theory]
+ [InlineData("\"y, , n\"")]
+ [InlineData("\",\"")]
+ [InlineData("\"y,n\"")]
+ public static void EnumWithEmptyStringMemberName_NonFlags_DoesNotParseCommaSeparatedValues(string json)
+ {
+ Assert.Throws(() => JsonSerializer.Deserialize(json, s_optionsWithStringEnumConverter));
+ }
+
+ public enum YesOrNoOrEmpty
+ {
+ [JsonStringEnumMemberName("y")]
+ Yes,
+
+ [JsonStringEnumMemberName("n")]
+ No,
+
+ [JsonStringEnumMemberName("")]
+ Empty,
+ }
+
+ [Fact]
+ public static void EnumWithEmptyStringMemberName_Flags_Throws()
+ {
+ string expectedExceptionMessage = $"Enum type '{nameof(YesOrNoOrEmptyFlags)}' uses unsupported identifier ''.";
+
+ InvalidOperationException ex = Assert.Throws(
+ () => JsonSerializer.Serialize(YesOrNoOrEmptyFlags.Yes, s_optionsWithStringEnumConverter));
+ Assert.Contains(expectedExceptionMessage, ex.Message);
+
+ ex = Assert.Throws(
+ () => JsonSerializer.Deserialize("\"y\"", s_optionsWithStringEnumConverter));
+ Assert.Contains(expectedExceptionMessage, ex.Message);
+ }
+
+ [Flags]
+ public enum YesOrNoOrEmptyFlags
+ {
+ [JsonStringEnumMemberName("y")]
+ Yes = 1,
+
+ [JsonStringEnumMemberName("n")]
+ No = 2,
+
+ [JsonStringEnumMemberName("")]
+ Empty = 4,
+ }
+
[Theory]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.SnakeCaseUpper)]
[InlineData("\"cAmElCaSe\"", EnumWithVaryingNamingPolicies.camelCase, JsonKnownNamingPolicy.SnakeCaseLower)]