I am trying to get the enum to display friendly name from the description (or any other attribute) in swagger and on response. Also trying to parse the friendly name set on the body/querystring in the controller action without trying a 400 BadRequest or any kinda validation error. What I have also noticed is that the custom generic JsonConverter I have is also not working properly. the ReadJson()
method isn't being called at all. How can I get this to work?
[JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
public enum SortDirectionType
{
[Description("asc")]
ASCENDING,
[Description("desc")]
DESCENDING
}
I'm trying to get swagger-ui to show asc & desc as the values in the dropdown as opposed to ASCENDING & DESCENDING. It means that I cannot use c.DescribeAllEnumsAsStrings()
. If I dont use it then the dropdown shows 0,1 as it should to represent the enum member values. Now I could use [EnumMember(Value="asc"]
attribute instead of [Description("asc")]
attribute. However then two things are happening:
- Either swagger-ui throws a client validation and highlights the field with red line around it
- Or if I try to call the endpoint outside of swagger it returns a 400 error.
- I'm able to use the 0,1 or ASCENDING, DESCENDING values successfully within the action
[FromBody]
argument. However that's not what is expected. I'm going to receive asc, desc and want to be able to parse that successfully on the body model and map it to the enum property. On the flip side when the json renders it should render the friendly name.
Additional Code:
public class JsonEnumConverter<T> : JsonConverter where T : struct, IComparable, IConvertible, IFormattable
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
var enumDescription = (string)reader.Value;
return enumDescription.GetEnumValueFromDescription<T>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
if (value != null)
{
if (value is Enum sourceEnum)
{
writer.WriteValue(sourceEnum.GetDescriptionFromEnumValue());
}
}
}
}
public static class EnumExtensions
{
public static string GetDescriptionFromEnumValue(this Enum @enum)
{
FieldInfo fi = @enum.GetType().GetField(@enum.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return @enum.ToString();
}
public static T GetEnumValueFromDescription<T>(this string description)
{
var type = typeof(T);
if (!type.IsEnum)
throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
{
if (attribute.Description == description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException($"No matching value for enum {nameof(T)} found from {description}.",$"{nameof(description)}"); // or return default(T);
}
}