Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@
<value>'{0}' is invalid within a JSON string. The string should be correctly escaped.</value>
</data>
<data name="UnsupportedEnumIdentifier" xml:space="preserve">
<value>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.</value>
<value>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.</value>
</data>
<data name="InvalidEndOfJsonNonPrimitive" xml:space="preserve">
<value>'{0}' is an invalid token type for the end of the JSON payload. Expected either 'EndArray' or 'EndObject'.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,26 @@ private bool TryParseNamedEnum(
{
#if NET
Dictionary<string, EnumFieldInfo>.AlternateLookup<ReadOnlySpan<char>> lookup = _enumFieldInfoIndex.GetAlternateLookup<ReadOnlySpan<char>>();
ReadOnlySpan<char> rest = source;
#else
Dictionary<string, EnumFieldInfo> 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<char> rest = source;
#else
ReadOnlySpan<char> rest = source.AsSpan();
#endif
ulong key = 0;
Expand Down Expand Up @@ -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(','))))
Comment thread
eiriktsarpalis marked this conversation as resolved.
{
// 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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1259,6 +1259,7 @@ public enum EnumWithInvalidMemberName1
Value
}

[Flags]
public enum EnumWithInvalidMemberName2
{
[JsonStringEnumMemberName("")]
Expand Down Expand Up @@ -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<YesOrNoOrEmpty>(json, s_optionsWithStringEnumConverter));
}

[Theory]
[InlineData("\"y, , n\"")]
[InlineData("\",\"")]
[InlineData("\"y,n\"")]
public static void EnumWithEmptyStringMemberName_NonFlags_DoesNotParseCommaSeparatedValues(string json)
{
Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<YesOrNoOrEmpty>(json, s_optionsWithStringEnumConverter));
}

public enum YesOrNoOrEmpty
{
[JsonStringEnumMemberName("y")]
Yes,

[JsonStringEnumMemberName("n")]
No,

[JsonStringEnumMemberName("")]
Comment thread
eiriktsarpalis marked this conversation as resolved.
Empty,
}

[Fact]
public static void EnumWithEmptyStringMemberName_Flags_Throws()
{
string expectedExceptionMessage = $"Enum type '{nameof(YesOrNoOrEmptyFlags)}' uses unsupported identifier ''.";

InvalidOperationException ex = Assert.Throws<InvalidOperationException>(
() => JsonSerializer.Serialize(YesOrNoOrEmptyFlags.Yes, s_optionsWithStringEnumConverter));
Assert.Contains(expectedExceptionMessage, ex.Message);

ex = Assert.Throws<InvalidOperationException>(
() => JsonSerializer.Deserialize<YesOrNoOrEmptyFlags>("\"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)]
Expand Down
Loading