-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathJsonStringEnumMemberConverter.cs
More file actions
132 lines (119 loc) · 4.45 KB
/
Copy pathJsonStringEnumMemberConverter.cs
File metadata and controls
132 lines (119 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
namespace System.Text.Json.Serialization
{
/// <summary>
/// <see cref="JsonConverterFactory"/> to convert enums to and from strings, respecting <see cref="EnumMemberAttribute"/> decorations. Supports nullable enums.
/// </summary>
public class JsonStringEnumMemberConverter : JsonConverterFactory
{
private readonly HashSet<Type>? _EnumTypes;
private readonly JsonStringEnumMemberConverterOptions? _Options;
/// <summary>
/// Initializes a new instance of the <see cref="JsonStringEnumMemberConverter"/> class.
/// </summary>
public JsonStringEnumMemberConverter()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonStringEnumMemberConverter"/> class.
/// </summary>
/// <param name="namingPolicy">
/// Optional naming policy for writing enum values.
/// </param>
/// <param name="allowIntegerValues">
/// True to allow undefined enum values. When true, if an enum value isn't
/// defined it will output as a number rather than a string.
/// </param>
public JsonStringEnumMemberConverter(JsonNamingPolicy? namingPolicy = null, bool allowIntegerValues = true)
: this(new JsonStringEnumMemberConverterOptions { NamingPolicy = namingPolicy, AllowIntegerValues = allowIntegerValues })
{
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonStringEnumMemberConverter"/> class.
/// </summary>
/// <param name="options"><see cref="JsonStringEnumMemberConverterOptions"/>.</param>
/// <param name="targetEnumTypes">Optional list of supported enum types to be converted. Specify <see langword="null"/> or empty to convert all enums.</param>
public JsonStringEnumMemberConverter(JsonStringEnumMemberConverterOptions options, params Type[] targetEnumTypes)
{
_Options = options ?? throw new ArgumentNullException(nameof(options));
if (targetEnumTypes != null && targetEnumTypes.Length > 0)
{
#if NETSTANDARD2_0
_EnumTypes = new HashSet<Type>();
#else
_EnumTypes = new HashSet<Type>(targetEnumTypes.Length);
#endif
foreach (Type enumType in targetEnumTypes)
{
if (enumType.IsEnum)
{
_EnumTypes.Add(enumType);
continue;
}
if (enumType.IsGenericType)
{
(bool IsNullableEnum, Type? UnderlyingType) = TestNullableEnum(enumType);
if (IsNullableEnum)
{
_EnumTypes.Add(UnderlyingType!);
continue;
}
}
throw new NotSupportedException($"Type {enumType} is not supported by JsonStringEnumMemberConverter. Only enum types can be converted.");
}
}
}
#pragma warning disable CA1062 // Validate arguments of public methods
/// <inheritdoc/>
public override bool CanConvert(Type typeToConvert)
{
// Don't perform a typeToConvert == null check for performance. Trust our callers will be nice.
return _EnumTypes != null
? _EnumTypes.Contains(typeToConvert)
: typeToConvert.IsEnum;
}
#pragma warning restore CA1062 // Validate arguments of public methods
/// <inheritdoc/>
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
(bool IsNullableEnum, Type? UnderlyingType) = TestNullableEnum(typeToConvert);
try
{
return (JsonConverter)Activator.CreateInstance(
typeof(JsonStringEnumMemberConverter<>).MakeGenericType(IsNullableEnum ? UnderlyingType! : typeToConvert),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object?[] { _Options },
culture: null)!;
}
catch (TargetInvocationException targetInvocationEx)
{
if (targetInvocationEx.InnerException != null)
throw targetInvocationEx.InnerException;
throw;
}
}
private static (bool IsNullableEnum, Type? UnderlyingType) TestNullableEnum(Type typeToConvert)
{
Type? UnderlyingType = Nullable.GetUnderlyingType(typeToConvert);
return (UnderlyingType?.IsEnum ?? false, UnderlyingType);
}
internal static ulong GetEnumValue(TypeCode enumTypeCode, object value)
{
return enumTypeCode switch
{
TypeCode.Int32 => (ulong)(int)value,
TypeCode.Int64 => (ulong)(long)value,
TypeCode.Int16 => (ulong)(short)value,
TypeCode.Byte => (byte)value,
TypeCode.UInt32 => (uint)value,
TypeCode.UInt64 => (ulong)value,
TypeCode.UInt16 => (ushort)value,
TypeCode.SByte => (ulong)(sbyte)value,
_ => throw new NotSupportedException($"Enum '{value}' of {enumTypeCode} type is not supported."),
};
}
}
}