You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 01:07:42