1 year ago

#76055

test-img

Vic F

JSON Converter changing case of values in netcoreapp3.1 web api

I have an enum extension that allows for enums to be transformed into their [Description] value, as in:

    private enum WithDescription
    {
        [Description("Nothing")] None = 0,
        [Description("Some of it")] Some,
        [Description("All of it")] All
    }
public static class EnumExtensions
{
    public static string GetDescription<T>(this T enumerationValue) where T : struct, Enum
    {
        var type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException($"{nameof(T)} must be of type Enum.");
        }

        var memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo.Length > 0)
        {
            var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs.Length > 0)
            {
                return ((DescriptionAttribute) attrs[0]).Description;
            }
        }

        return enumerationValue.ToString();
    }

    public static T GetEnumByDescription<T>(this string text) where T : struct, Enum
    {
        var type = typeof(T);
        if (!type.IsEnum)
        {
            throw new ArgumentException($"{nameof(T)} must be of type Enum.");
        }

        MemberInfo[] members = type.GetMembers(BindingFlags.Public | BindingFlags.Static);
        foreach (MemberInfo member in members)
        {
            var attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attrs.Length > 0)
            {
                for (int i = 0; i < attrs.Length; i++)
                {
                    string description = ((DescriptionAttribute) attrs[i]).Description;
                    if (text.Equals(description, StringComparison.OrdinalIgnoreCase))
                    {
                        return (T) Enum.Parse(type, member.Name, true);
                    }
                }
            }

            if (member.Name.Equals(text, StringComparison.OrdinalIgnoreCase))
            {
                return (T) Enum.Parse(type, member.Name, true);
            }
        }

        return default;
    }

    public static IEnumerable<string> GetDescriptions<T>() where T : struct, Enum
    {
        MemberInfo[] members = typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Static);
        foreach (MemberInfo member in members)
        {
            var attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
            if (attrs.Length > 0)
            {
                for (int i = 0; i < attrs.Length; i++)
                {
                    string description = ((DescriptionAttribute) attrs[i]).Description;

                    yield return description;
                }
            }
            else
            {
                yield return member.Name;
            }
        }
    }
}

And, I have a custom System.Text.Json converter:

public class EnumDescriptionConverterFactory : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert) => typeToConvert.IsEnum;

    public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) =>
        (JsonConverter?) Activator.CreateInstance(
            typeof(EnumConverterInner<>).MakeGenericType(new Type[] {typeToConvert}),
            BindingFlags.Instance | BindingFlags.Public,
            binder: null,
            args: null,
            culture: null);

    private class EnumConverterInner<T> : JsonConverter<T> where T : struct, Enum
    {
        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            string? propertyName = reader.GetString();

            MemberInfo[] members = typeToConvert.GetMembers(BindingFlags.Public | BindingFlags.Static);
            foreach (MemberInfo member in members)
            {
                var attrs = member.GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attrs.Length > 0)
                {
                    foreach (var t in attrs)
                    {
                        string description = ((DescriptionAttribute) t).Description;
                        if (propertyName?.Equals(description, StringComparison.OrdinalIgnoreCase) ?? false)
                        {
                            return (T) Enum.Parse(typeToConvert, member.Name, true);
                        }
                    }
                }

                if (member.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))
                {
                    return (T) Enum.Parse(typeToConvert, member.Name, true);
                }
            }

            if (!Enum.TryParse(propertyName, false, out T result) &&
                !Enum.TryParse(propertyName, true, out result))
            {
                throw new JsonException(
                    $"Unable to convert \"{propertyName}\" to Enum \"{typeToConvert}\".");
            }

            return result;
        }

        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
        {
            Type type = value.GetType();

            var memberInfo = type.GetMember(value.ToString());
            if (memberInfo.Length > 0)
            {
                var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs.Length > 0)
                {
                    var description = ((DescriptionAttribute) attrs[0]).Description;
                    writer.WriteStringValue(options.PropertyNamingPolicy?.ConvertName(description) ?? description);
                }
                else
                {
                    writer.WriteStringValue(options.PropertyNamingPolicy?.ConvertName(value.ToString()) ??
                                            value.ToString());
                }
            }
        }
    }

This code all works perfectly in every way I could test it, but when I plug it into a netcoreapp3.1 web api application, it converts the description to all lowercase in the output. In my tests, I get "Some of it" and in actual output from an API endpoint, I get "some of it"

In my ConfigureServices I have:

            services.AddControllers(options =>
                options.Filters.Add(new HttpResponseExceptionFilter())).AddJsonOptions(GetJsonOptions());

and the function being called is:

        private static Action<JsonOptions> GetJsonOptions()
        {
            return options =>
            {
                options.JsonSerializerOptions.IgnoreNullValues = true;
                options.JsonSerializerOptions.MaxDepth = 64;
                options.JsonSerializerOptions.Converters.Add(new EnumDescriptionConverterFactory());
            };
        }

As I wrote, I'm sure it's "working" because the transformation takes place, but the output is always lower case.

Anyone know why this might be happening or what I could do about it?

c#

asp.net-core-webapi

asp.net-core-3.1

system.text.json

json-serialization

0 Answers

Your Answer

Accepted video resources