使用System.Text.Json反序列化含连字符的EnumMember枚举值问题
解决System.Text.Json反序列化带连字符EnumMember枚举的问题
1. 正确定义枚举及基础配置
首先确保枚举正确标记EnumMember特性,并指定JsonStringEnumConverter(需引用System.Text.Json和System.Runtime.Serialization命名空间):
using System.Runtime.Serialization; using System.Text.Json.Serialization; [JsonConverter(typeof(JsonStringEnumConverter))] public enum CheckStatus { [EnumMember(Value = "newFile")] NewFile, [EnumMember(Value = "check-status")] CheckStatus }
注意:.NET 5及以上版本的
JsonStringEnumConverter默认支持读取EnumMember特性值,若使用低于5.0的版本,需要自定义转换器。
2. 自定义JsonConverter兼容低版本.NET
如果默认转换器不生效(比如低版本.NET),可以自定义转换器处理带连字符的枚举值:
using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.Serialization; using System.Text.Json; using System.Text.Json.Serialization; public class CustomEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum { private readonly Dictionary<string, TEnum> _enumValueMap = new(); public CustomEnumConverter() { var enumType = typeof(TEnum); foreach (var member in enumType.GetMembers(BindingFlags.Public | BindingFlags.Static)) { var enumMemberAttr = member.GetCustomAttribute<EnumMemberAttribute>(); if (enumMemberAttr != null && Enum.TryParse(enumType, member.Name, out var enumValue)) { _enumValueMap.Add(enumMemberAttr.Value, (TEnum)enumValue); } // 可选:同时添加枚举名称作为匹配项 _enumValueMap.TryAdd(member.Name, (TEnum)enumValue); } } public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var value = reader.GetString(); if (value == null || !_enumValueMap.TryGetValue(value, out var enumValue)) { throw new JsonException($"无法将值 '{value}' 转换为枚举 {typeToConvert.Name}"); } return enumValue; } public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) { var enumName = value.ToString(); var member = typeof(TEnum).GetMember(enumName)[0]; var enumMemberAttr = member.GetCustomAttribute<EnumMemberAttribute>(); writer.WriteStringValue(enumMemberAttr?.Value ?? enumName); } }
然后将自定义转换器应用到枚举上:
[JsonConverter(typeof(CustomEnumConverter<CheckStatus>))] public enum CheckStatus { [EnumMember(Value = "newFile")] NewFile, [EnumMember(Value = "check-status")] CheckStatus }
3. 修复IModelBinder实现(ASP.NET Core场景)
如果是在ASP.NET Core中使用IModelBinder做模型绑定,需要调整FileStatusModelBinder的逻辑,确保能识别带连字符的EnumMember值:
using Microsoft.AspNetCore.Mvc.ModelBinding; using System; using System.Reflection; using System.Runtime.Serialization; using System.Threading.Tasks; public class FileStatusModelBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext)); var modelName = bindingContext.ModelName; var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName); if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask; bindingContext.ModelState.SetModelValue(modelName, valueProviderResult); var value = valueProviderResult.FirstValue; if (string.IsNullOrEmpty(value)) return Task.CompletedTask; var enumType = bindingContext.ModelType; // 优先匹配EnumMember特性值 foreach (var member in enumType.GetMembers(BindingFlags.Public | BindingFlags.Static)) { var enumMemberAttr = member.GetCustomAttribute<EnumMemberAttribute>(); if (enumMemberAttr != null && string.Equals(enumMemberAttr.Value, value, StringComparison.OrdinalIgnoreCase)) { if (Enum.TryParse(enumType, member.Name, out var enumValue)) { bindingContext.Result = ModelBindingResult.Success(enumValue); return Task.CompletedTask; } } } // 备选:直接解析枚举名称 if (Enum.TryParse(enumType, value, ignoreCase: true, out var result)) { bindingContext.Result = ModelBindingResult.Success(result); } else { bindingContext.ModelState.TryAddModelError(modelName, $"无法将值 '{value}' 转换为枚举 {enumType.Name}"); } return Task.CompletedTask; } }
可以在Program.cs中配置全局模型绑定:
builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new BinderTypeModelBinderProvider(typeof(CheckStatus), new FileStatusModelBinder())); });
4. 验证反序列化
测试JSON反序列化:
var json = "\"check-status\""; var status = JsonSerializer.Deserialize<CheckStatus>(json); // status 应等于 CheckStatus.CheckStatus
或者在API接口中接收参数:
[HttpPost] public IActionResult Post([FromBody] CheckStatus status) { return Ok(status); }
传入"check-status"即可正确绑定到对应枚举值。
内容的提问来源于stack exchange,提问作者Reece Smith
相关产品推荐
相关产品推荐

