.NET 3.5中如何让DataContractJsonSerializer反序列化不匹配类型时失败?
我完全理解你的困扰——DataContractJsonSerializer的这种“宽容”行为确实很坑人:当JSON结构和目标类型不匹配时,它不会抛出异常,反而会默默返回一个所有属性都是默认值的对象,直接跳过了你原本的空值判断逻辑。下面给你几个可行的解决方案,适配.NET 3.5的约束:
核心思路:先判断JSON类型,再选择序列化器
因为流只能读取一次,且序列化器不会主动报错,我们需要先捕获完整的JSON内容,通过特征判断它属于哪种类型,再针对性反序列化。
方案1:通过特有属性判断类型(适合API返回的两种JSON有明显区别字段)
比如你的ResponseDto有一个独有的属性OrderId,而SecondResponseDto没有,那我们可以先读取JSON字符串,检查是否包含这个属性名,再决定用哪个序列化器:
ResponseDto Get<ResponseDto>(string requestUrl) { try { var request = (HttpWebRequest)WebRequest.Create(requestUrl); // 省略请求头、超时等设置代码 using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { // 先把响应流读成字符串(解决流只能读一次的问题) string jsonContent; using (var streamReader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { jsonContent = streamReader.ReadToEnd(); } // 检查是否包含ResponseDto的特有属性(替换成你的实际属性名) bool isType1Response = jsonContent.Contains("\"OrderId\":"); if (isType1Response) { var serializer = new DataContractJsonSerializer(typeof(ResponseDto)); using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent))) { var responseDto = (ResponseDto)serializer.ReadObject(memoryStream); // 额外验证:确保不是全默认值的空对象(根据你的Dto结构调整) if (IsValidResponseDto(responseDto)) { return responseDto; } } } // 处理SecondResponseDto的情况 var otherSerializer = new DataContractJsonSerializer(typeof(SecondResponseDto)); using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent))) { var otherResponse = (SecondResponseDto)otherSerializer.ReadObject(memoryStream); HandleOtherResponse(otherResponse); } return default(ResponseDto); } } } catch (WebException e) { // 异常处理:日志、重试等 return default(ResponseDto); } } // 自定义验证方法:判断ResponseDto是否是有效的非默认对象 bool IsValidResponseDto(ResponseDto dto) { // 示例:假设OrderId是int类型,有效响应的OrderId不能为0 return dto.OrderId != 0; // 多属性验证示例:return !string.IsNullOrEmpty(dto.Name) && dto.TotalAmount > 0; }
方案2:通过类型标识判断(如果API返回的JSON包含类型字段)
如果API返回的JSON里有明确的类型标记(比如"ResponseType": "Type1"),这种方法更可靠:
ResponseDto Get<ResponseDto>(string requestUrl) { try { var request = (HttpWebRequest)WebRequest.Create(requestUrl); using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { string jsonContent; using (var streamReader = new StreamReader(response.GetResponseStream(), Encoding.UTF8)) { jsonContent = streamReader.ReadToEnd(); } // 先反序列化成字典,读取类型标识 var dictSerializer = new DataContractJsonSerializer(typeof(Dictionary<string, object>)); string responseType = string.Empty; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent))) { var jsonDict = (Dictionary<string, object>)dictSerializer.ReadObject(ms); jsonDict.TryGetValue("ResponseType", out object typeValue); responseType = typeValue?.ToString() ?? string.Empty; } if (responseType == "Type1") { var serializer = new DataContractJsonSerializer(typeof(ResponseDto)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent))) { var responseDto = (ResponseDto)serializer.ReadObject(ms); return IsValidResponseDto(responseDto) ? responseDto : default(ResponseDto); } } else { var otherSerializer = new DataContractJsonSerializer(typeof(SecondResponseDto)); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent))) { var otherResponse = (SecondResponseDto)otherSerializer.ReadObject(ms); HandleOtherResponse(otherResponse); } return default(ResponseDto); } } } } catch (WebException e) { // 异常处理逻辑 return default(ResponseDto); } }
关键注意事项
- 流的处理:必须先把响应流读成字符串或内存流,因为
HttpWebResponse的流只能读取一次,直接用序列化器读过后就无法再复用。 - 编码一致性:确保
StreamReader的编码和API返回的JSON编码一致(通常是UTF-8)。 - 有效性验证:即使通过特征判断了类型,也要加上
IsValidResponseDto这类验证,避免因为JSON缺失必填字段导致的全默认值对象。 - DataContract标记:确保你的
ResponseDto和SecondResponseDto的属性都正确标记了[DataMember],否则序列化器会忽略未标记的属性,导致误判。
内容的提问来源于stack exchange,提问作者Myar
相关产品推荐
相关产品推荐

