2

Looking for help with Newtonsoft Json on asp.net core 2.2. I have a JsonEnumConverter<T> which was responsible for serializing/deserializing values from DescriptionAttribute from an Enum type. It was working fine until about 2 weeks ago and now it has completely stopped working.

here's what I have:

//From PerformersController: 
public async Task<ActionResult<PagedPerformers>> GetPagedPerformersAsync([FromQuery] PerformerRequest performerRequest) { ... }

    [JsonObject]
    public class PerformerRequest : PageRequest
    {
        [FromQuery(Name = "performer_id")]
        [JsonProperty(PropertyName = "performer_id", Order = 1)]
        public override string Id { get; set; }
        ....
    }

    [JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
    public enum SortDirectionType
    {
        [Description("asc")]
        ASCENDING,
        [Description("desc")]
        DESCENDING
    }

    public abstract class PageRequest
    {
        [FromQuery(Name = "page")]
        [JsonProperty("page")]
        public int Page { get; set; }

        [FromQuery(Name = "limit")]
        [JsonProperty("limit")]
        public int PageSize { get; set; } = 100;

        [FromQuery(Name = "sort_field")]
        [JsonProperty("sort_field")]
        public string SortField { get; set; } //= "Id";

        [FromQuery(Name = "sort_dir")] [JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
        [JsonProperty("sort_dir")]
        public SortDirectionType SortDirection { get; set; }

        [FromQuery(Name = "id")]
        [JsonProperty("id")]
        public virtual string Id { get; set; }
    }

    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);
        }
}

this was working absolutely fine until recently. Now I'm not sure whats going on I get ValidationProblemDetails response right away. If I suppress asp.net core 2.2 model state invalid filter then modelState.IsValid will still have false. If I put a breakpoint in ReadJson of my JsonEnumConverter it wont even hit. Even tried to set JsonSerializerSettings in startup with no success or luck. Have already tried replacing Description with EnumMember and StringEnumConverter. Still the same issue. Seems like there is some issue with ModelBinder and Json.NET not playing well with each other.

NOTE: This issue is happening on ASP.NET Core 2.2. Suggesting solutions for 3.0 is not helpful!!

8
  • 1
    Did you move to asp.net-core-3.0? If so, Json.NET is no longer the default serializer, instead Microsoft introduced their own homebrew serializer system.text.json. See: Where did IMvcBuilder AddJsonOptions go in .Net Core 3.0? which explains how to revert back to Json.NET. Commented Dec 19, 2019 at 4:49
  • I did not. still using <TargetFramework>netcoreapp2.2</TargetFramework>. Commented Dec 19, 2019 at 6:11
  • If you are using aspnet core 3, you can try this library : github.com/StefH/System.Text.Json.EnumExtensions Commented Jan 6, 2020 at 18:56
  • @StefHeyenrath using asp.net core 2.2...While this is a great solution it doesn't really help my cause. Thanks for your help! Commented Jan 6, 2020 at 19:56
  • 1
    @StefHeyenrath if you would like to upgrade your comment to answer I'll mark it as solved. Commented Oct 2, 2020 at 15:34

1 Answer 1

2

If you are using aspnet core 3 / netstandard 2.1, you can try this library https://github.com/StefH/System.Text.Json.EnumExtensions which defines some extensions to the JsonStringEnumConverter to support attributes like EnumMember, Display and Description.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.