It’s common to have description attributes on Enum types. This allows adding a longer description string for each Enum value. In one of my recent projects, I have a Web API that needs to accept this description value from the JSON request body and map to the correct Enum value. In this post, I’m going to add a custom JSON converter that will allow de-serialising the description value back to the Enum value and serialising an Enum value to its description value and.
Let’s say we have an Enum like this
public enum MonthOfYear { [Description("First month of the year")] January, [Description("Second month of the year")] February, [Description("Third month of the year")] March // ... and so on }
And a sample request model that looks like this
public class SampleRequest
{
public MonthOfYear MonthOfYear { get; set; }
}
If we try to de-serialise the next JSON string to SampleRequest, it would work
{
"MonthOfYear": 2
}
{
"MonthOfYear": "March"
}
However, if we use this JSON string, it will throw an exception because it cannot parse to the Enum
{
"MonthOfYear": "Third month of the year"
}
Next, we’re going to add a custom JSON converter to support this. In this post, I showed how to add extension method to get the description attribute value for an Enum. We’re going to be reusing this in the custom JSON converter.
public class EnumConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var enumType = Nullable.GetUnderlyingType(objectType) ?? objectType;
return enumType.IsEnum;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value is long)
return Enum.ToObject(objectType, reader.Value);
string description = (string)reader.Value;
if (description is null) return null;
foreach (var field in objectType.GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
{
if (attribute.Description == description)
return field.GetValue(null);
}
else
{
if (field.Name == description)
return field.GetValue(null);
}
}
throw new ArgumentException("Not found.", nameof(description));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((Enum)value).GetDescription() ?? value.ToString());
}
}
In the CanConvert, we only allow if type is an Enum. ReadJson will be called when de-serialising JSON string and WriteJson will be called when serialising JSON string. We should now be able to de-serialise the previous JSON string using this converter.
And it also works when serialising the request model.