如何在System.Text.Json中实现带自定义回退的JsonStringEnumConverter
在System.Text.Json中实现未知枚举值自动回退到默认值
针对.NET 6中使用System.Text.Json处理外部API返回的JSON字符串,将未定义的枚举值自动反序列化为MyEnum.Unknown的需求,有两种简便可行的实现方案:
方案一:通用枚举转换器工厂(支持所有枚举类型)
这种方案适合需要处理多个枚举类型的场景,通过自定义JsonConverterFactory为每个枚举类型生成对应的转换器,统一处理未知值回退逻辑。
1. 泛型枚举转换器
首先定义一个泛型转换器,负责单个枚举类型的反序列化与序列化:
public class GenericEnumConverter<T> : JsonConverter<T> where T : struct, Enum { private readonly T _defaultValue; public GenericEnumConverter(T defaultValue) { _defaultValue = defaultValue; } public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 非字符串类型直接返回默认值 if (reader.TokenType != JsonTokenType.String) { return _defaultValue; } var enumString = reader.GetString(); // 尝试解析枚举,同时验证是否为已定义的枚举成员 if (Enum.TryParse(enumString, ignoreCase: options.PropertyNameCaseInsensitive, out T result) && Enum.IsDefined(typeof(T), result)) { return result; } // 解析失败或值未定义时返回默认值 return _defaultValue; } public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { writer.WriteStringValue(value.ToString()); } }
2. 转换器工厂
实现JsonConverterFactory,用于判断类型是否为枚举,并创建对应的泛型转换器:
public class FallbackEnumConverterFactory : JsonConverterFactory { private readonly object _defaultValue; public FallbackEnumConverterFactory(object defaultValue) { _defaultValue = defaultValue; } public override bool CanConvert(Type typeToConvert) { return typeToConvert.IsEnum; } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var converterType = typeof(GenericEnumConverter<>).MakeGenericType(typeToConvert); return (JsonConverter)Activator.CreateInstance(converterType, _defaultValue)!; } }
3. 使用方式
在JsonSerializerOptions中注册该工厂,并指定回退的默认值:
var options = new JsonSerializerOptions { Converters = { new FallbackEnumConverterFactory(MyEnum.Unknown) }, PropertyNameCaseInsensitive = true // 根据API返回的大小写情况选择是否启用 }; // 反序列化示例 var json = "{\"EnumProperty\": \"UndefinedValue\"}"; var targetObject = JsonSerializer.Deserialize<YourModel>(json, options); // targetObject.EnumProperty 会被设置为 MyEnum.Unknown
方案二:针对特定枚举的转换器(仅处理单个枚举)
如果只需要处理MyEnum这一个枚举类型,可以直接编写针对性的转换器,代码更简洁:
public class MyEnumConverter : JsonConverter<MyEnum> { public override MyEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.String) return MyEnum.Unknown; var enumValue = reader.GetString(); // 解析并验证是否为已定义的枚举成员 if (Enum.TryParse(enumValue, ignoreCase: options.PropertyNameCaseInsensitive, out MyEnum result) && Enum.IsDefined(result)) { return result; } return MyEnum.Unknown; } public override void Write(Utf8JsonWriter writer, MyEnum value, JsonSerializerOptions options) { writer.WriteStringValue(value.ToString()); } }
使用方式
直接将转换器添加到JsonSerializerOptions中:
var options = new JsonSerializerOptions { Converters = { new MyEnumConverter() }, PropertyNameCaseInsensitive = true };
关键注意点
Enum.TryParse默认允许解析未在枚举中定义的整数值对应的字符串(比如枚举未定义Value4,但字符串"4"会被解析为对应的整数值),因此必须配合Enum.IsDefined验证,确保只有已定义的枚举成员才会被返回。- 如果API返回的枚举值大小写与定义不一致,记得启用
PropertyNameCaseInsensitive或在Enum.TryParse中手动设置ignoreCase: true。
内容的提问来源于stack exchange,提问作者scharnyw
相关产品推荐
相关产品推荐

