C#使用Newtonsoft反序列化类型不匹配未抛预期异常如何优雅处理
核心原因
Newtonsoft.Json 默认反序列化行为是:当 JSON 字段值类型与实体类属性类型不匹配时,会静默将属性赋值为null,不会主动抛出异常,因此只有访问属性成员时才会触发空引用异常。
可选解决方案
方案1:反序列化阶段启用严格校验(推荐用于严格校验场景)
直接配置反序列化参数,在类型不匹配时直接抛出异常,将错误提前到反序列化阶段处理:
var deserializeSettings = new JsonSerializerSettings { Error = (sender, args) => { if (args.ErrorContext.Error is JsonSerializationException) { // 可自定义错误信息,明确告知哪个字段格式错误 throw new InvalidOperationException($"字段[{args.ErrorContext.Member}]格式不合法:{args.ErrorContext.Error.Message}", args.ErrorContext.Error); } args.ErrorContext.Handled = false; }, // 可选配置:遇到JSON中存在实体类没有的字段时也抛出异常 MissingMemberHandling = MissingMemberHandling.Error }; using (StreamReader r = new StreamReader(filePath)) { string json = r.ReadToEnd(); // 反序列化时传入配置 EmailStructureRequestModel emailStructure = JsonConvert.DeserializeObject<EmailStructureRequestModel>(json, deserializeSettings); // ...后续逻辑 }
配置后当Cc/Bcc传空字符串时,反序列化阶段就会直接抛出格式错误,不用等到访问属性时才处理。
方案2:自定义类型转换器(推荐用于兼容空字符串场景)
如果希望空字符串自动转为空列表而不是抛异常,可以自定义转换器适配不规范的输入:
- 先定义转换器
public class StringToListConverter : JsonConverter<List<string>> { public override List<string> ReadJson(JsonReader reader, Type objectType, List<string> existingValue, bool hasExistingValue, JsonSerializer serializer) { // 如果输入是字符串类型 if (reader.TokenType == JsonToken.String) { string strValue = reader.Value?.ToString(); // 空字符串返回空列表,如果希望非空字符串也报错可以在这里加抛出逻辑 return string.IsNullOrWhiteSpace(strValue) ? new List<string>() : throw new JsonSerializationException("预期为数组类型,实际为非空字符串"); } // 正常数组走默认转换逻辑 return serializer.Deserialize<List<string>>(reader); } public override void WriteJson(JsonWriter writer, List<string> value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
- 在实体类属性上挂载转换器
[JsonProperty(PropertyName = "Cc")] [JsonConverter(typeof(StringToListConverter))] public List<string> Cc { get; set; } [JsonProperty(PropertyName = "Bcc")] [JsonConverter(typeof(StringToListConverter))] public List<string> Bcc { get; set; }
配置后空字符串会自动转为空列表,访问Cc.Count不会再触发空引用异常。
方案3:实体类兜底初始化(最简单的兜底方案)
在实体类中初始化集合,属性赋值时自动兜底为空列表,从根源避免空引用:
public class EmailStructureRequestModel { public EmailStructureRequestModel() { // 构造时初始化所有集合属性 To = new List<string>(); Cc = new List<string>(); Bcc = new List<string>(); } [JsonProperty(PropertyName = "Sender", Required = Required.Always)] public EmailSender Sender { get; set; } [JsonProperty(PropertyName = "to", Required = Required.Always)] public List<string> To { get; set; } private List<string> _cc; [JsonProperty(PropertyName = "Cc")] public List<string> Cc { get => _cc; // 赋值为null时自动兜底为空列表 set => _cc = value ?? new List<string>(); } private List<string> _bcc; [JsonProperty(PropertyName = "Bcc")] public List<string> Bcc { get => _bcc; set => _bcc = value ?? new List<string>(); } [JsonProperty(PropertyName = "Content", Required = Required.Always)] public EmailContent Content { get; set; } }
内容的提问来源于stack exchange,提问作者davidvera
相关产品推荐
相关产品推荐

