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

如何使用System.Text.Json将FlagsAttribute枚举序列化为数组?

System.Text.Json 序列化Flags枚举为字符串数组方案

内置JsonStringEnumConverter对标记[Flags]特性的枚举,默认会将多值组合序列化为逗号分隔的单个字符串(如"Red, Black"),要输出["Red", "Black"]格式的数组,自定义通用转换器即可实现,同时兼容反序列化两种格式。

自定义转换器实现

using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

public class JsonStringEnumFlagsConverter : JsonConverterFactory
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeToConvert.IsEnum && typeToConvert.IsDefined(typeof(FlagsAttribute), false);
    }

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
    {
        var converterType = typeof(FlagsEnumConverterInner<>).MakeGenericType(typeToConvert);
        return (JsonConverter)Activator.CreateInstance(converterType)!;
    }

    private class FlagsEnumConverterInner<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
    {
        private readonly JsonStringEnumConverter _baseEnumConverter = new();
        private readonly TEnum[] _validFlags = Enum.GetValues<TEnum>()
            .Where(e => !e.Equals(default(TEnum)) && IsPowerOfTwo(Convert.ToUInt64(e)))
            .ToArray();

        public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var innerConverter = _baseEnumConverter.CreateConverter(typeof(TEnum), options);
            return reader.TokenType switch
            {
                JsonTokenType.StartArray => ReadArray(ref reader, options, innerConverter),
                JsonTokenType.String => (TEnum)innerConverter.Deserialize(ref reader, typeof(TEnum), options)!,
                _ => throw new JsonException($"不支持的令牌类型 {reader.TokenType} 反序列化Flags枚举")
            };
        }

        private TEnum ReadArray(ref Utf8JsonReader reader, JsonSerializerOptions options, JsonConverter innerConverter)
        {
            ulong result = 0;
            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndArray) break;
                var singleFlag = (TEnum)innerConverter.Deserialize(ref reader, typeof(TEnum), options)!;
                result |= Convert.ToUInt64(singleFlag);
            }
            return (TEnum)Enum.ToObject(typeof(TEnum), result);
        }

        public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
        {
            writer.WriteStartArray();
            var innerConverter = _baseEnumConverter.CreateConverter(typeof(TEnum), options);
            ulong numericVal = Convert.ToUInt64(value);
            
            if (numericVal == 0)
            {
                innerConverter.Serialize(writer, value, options);
            }
            else
            {
                foreach (var flag in _validFlags)
                {
                    if (value.HasFlag(flag))
                    {
                        innerConverter.Serialize(writer, flag, options);
                    }
                }
            }
            
            writer.WriteEndArray();
        }

        private static bool IsPowerOfTwo(ulong val) => val != 0 && (val & (val - 1)) == 0;
    }
}

使用方式

  • 全局生效:配置序列化选项时加入转换器,所有标记[Flags]的枚举都会自动按数组格式序列化
var serializeOptions = new JsonSerializerOptions
{
    Converters = { new JsonStringEnumFlagsConverter() }
};
  • 单属性生效:给对应枚举属性添加特性,仅对该属性生效
public class Product
{
    public string Name { get; set; }
    
    [JsonConverter(typeof(JsonStringEnumFlagsConverter))]
    public ColorTag Tags { get; set; }
}

[Flags]
public enum ColorTag
{
    None = 0,
    Red = 1,
    Black = 2,
    Blue = 4
}

效果说明

序列化值ColorTag.Red | ColorTag.Black时,输出结果为["Red","Black"];反序列化时既支持读取数组格式,也兼容默认的逗号分隔字符串格式,不会影响旧数据解析。

注:代码中默认对0值枚举输出对应的名称(如["None"]),如果不需要0值输出,可以修改Write方法中numericVal == 0分支的逻辑,直接跳过写入即可。

内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 07:51:26