F#如何将对象序列化为JSON:排除默认值且保留枚举名称
方案1:修复现有DataContractJsonSerializer实现(改造成本最低)
你之前的代码存在两个明显问题:一是实例化的DataContractJsonSerializerSettings完全没有使用,二是没有开启枚举序列化为字符串的配置。调整后的代码如下:
let json<'t> (myObj:'t) = use ms = new MemoryStream() let settings = DataContractJsonSerializerSettings( // 开启枚举转字符串输出 SerializeEnumsAsStrings = true, // 不输出默认值的属性,满足未赋值属性忽略的要求 EmitDefaultValues = false ) let serialiser = DataContractJsonSerializer(typeof<'t>, settings) serialiser.WriteObject(ms, myObj) // 注意这里建议用UTF8而非Default编码,避免不同环境编码不一致导致乱码 Encoding.UTF8.GetString(ms.ToArray())
这个方案不需要调整模型代码,也不需要处理额外的类型转换问题,直接可以满足你的三个需求。
方案2:解决System.Text.Json的类型转换报错
这个报错的根本原因是Open API Generator生成的模型中,可空枚举的私有字段_typeCode默认被初始化为0(对应枚举的底层int类型默认值),而非可空值类型的默认值null。而你的枚举定义大概率没有值为0的成员,导致枚举转换器尝试转换不存在的枚举值时抛出类型转换错误。
有两种修复方式:
方式A:自定义可空枚举转换器处理非法值
先实现一个通用的可空枚举转换器,遇到未定义的枚举值时直接输出null,配合忽略null值的配置即可自动跳过未赋值的属性:
public class CustomNullableEnumConverter<TEnum> : JsonConverter<TEnum?> where TEnum : struct, Enum { public override TEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); public override void Write(Utf8JsonWriter writer, TEnum? value, JsonSerializerOptions options) { if (!value.HasValue || !Enum.IsDefined(value.Value.GetType(), value.Value)) { writer.WriteNullValue(); return; } writer.WriteStringValue(Enum.GetName(value.Value.GetType(), value.Value)); } }
然后在序列化配置中注册转换器即可:
let options = JsonSerializerOptions() // 改为忽略null值,非法枚举值会被转换器输出为null后自动跳过 options.DefaultIgnoreCondition <- JsonIgnoreCondition.WhenWritingNull // 注册你的自定义可空枚举转换器 options.Converters.Add(CustomNullableEnumConverter<ExpressPackageReference.TypeCodeEnum>()) // 其他普通枚举仍用默认的字符串转换器 options.Converters.Add(JsonStringEnumConverter(JsonNamingPolicy.CamelCase)) let jsonString:string = JsonSerializer.Serialize(shipmentRequest, options)
方式B:调整Open API Generator生成配置
在生成模型的命令中添加参数,让生成器自动给所有可空枚举属性设置默认值为null,并且添加忽略默认值的序列化特性,从根源避免非法值的问题。
内容的提问来源于stack exchange,提问作者Martin Thompson
相关产品推荐
相关产品推荐

