Search code examples
c#json.net-coreserializationsystem.text.json

.NET C# Enum Json deserialize with a dash in the EnumMember value without newtonsoft


I am using System.Text.Json.Serialization I am trying to deserialize a member with a dash in the EnumMember value CheckStatus.

  [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
  public enum CheckStatus
  {
       [System.Runtime.Serialization.EnumMember(Value = "newFile")]
       NewFile,

       [System.Runtime.Serialization.EnumMember(Value = "check-status")]
       CheckStatus,
  }

I have written a BindModelAsync

   public class FileStatusModelBuilder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {

            var rawData = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).FirstValue;
            rawData = JsonSerializer.Serialize(rawData);
            CheckStatus result = JsonSerializer.Deserialize<CheckStatus>(rawData);
            bindingContext.Result = ModelBindingResult.Success(result);
            return Task.CompletedTask;
        }
    }

When passing in newFile the binding works and will bind to NewFile, but as soon as I use check-status I get the following error.

The JSON value could not be converted to [...].CheckStatus. Path: $ | LineNumber: 0 | BytePositionInLine: 15.

I don't want to use newtonsoft but would like to be able to have an enum with a dash in the EnumMember value.


Solution

  • System.Text.Json still seems to not support System.Runtime.Serialization (and it seems it will not be, based on this github issue), so you can implement custom serializer, for example this workaround from this github issue on the topic:

    [System.Text.Json.Serialization.JsonConverter(typeof(JsonStringEnumConverterEx<CheckStatus>))]
    public enum CheckStatus
    {
        [EnumMember(Value = "newFile")]
        NewFile,
    
        [EnumMember(Value = "check-status")]
        CheckStatus,
    }
    
    public class JsonStringEnumConverterEx<TEnum> : System.Text.Json.Serialization.JsonConverter<TEnum>
        where TEnum : struct, System.Enum
    {
    
        private readonly Dictionary<TEnum, string> _enumToString = new();
        private readonly Dictionary<string, TEnum> _stringToEnum = new();
        private readonly Dictionary<int, TEnum> _numberToEnum = new();
    
        public JsonStringEnumConverterEx()
        {
            var type = typeof(TEnum);
    
            foreach (var value in Enum.GetValues<TEnum>())
            {
                var enumMember = type.GetMember(value.ToString())[0];
                var attr = enumMember.GetCustomAttributes(typeof(EnumMemberAttribute), false)
                    .Cast<EnumMemberAttribute>()
                    .FirstOrDefault();
    
                _stringToEnum.Add(value.ToString(), value);
                var num = Convert.ToInt32(type.GetField("value__")
                    .GetValue(value));
                if (attr?.Value != null)
                {
                    _enumToString.Add(value, attr.Value);
                    _stringToEnum.Add(attr.Value, value);
                    _numberToEnum.Add(num, value);
                }
                else
                {
                    _enumToString.Add(value, value.ToString());
                }
            }
        }
    
        public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var type = reader.TokenType;
            if (type == JsonTokenType.String)
            {
                var stringValue = reader.GetString();
                if (_stringToEnum.TryGetValue(stringValue, out var enumValue))
                {
                    return enumValue;
                }
            }
            else if (type == JsonTokenType.Number)
            {
                var numValue = reader.GetInt32();
                _numberToEnum.TryGetValue(numValue, out var enumValue);
                return enumValue;
            }
    
            return default;
        }
    
        public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(_enumToString[value]);
        }
    }