如何使用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
相关产品推荐
相关产品推荐

