Blazor添加AddNewtonsoftJson后,JsonConvert反序列化ValidationResult报错
解决Blazor中Patch配置与ValidationResult反序列化冲突问题
问题场景
在Blazor应用中,为实现Patch功能配置了builder.Services.AddControllers().AddNewtonsoftJson(),但出现以下矛盾:
- 接口无验证错误时运行正常
- 返回验证响应时,
JsonConvert.DeserializeObject抛出异常,提示无法找到System.ComponentModel.DataAnnotations.ValidationResult的合适构造函数 - 移除
.AddNewtonsoftJson()后,验证消息可正常返回,但Patch功能失效
相关代码与异常信息
前端响应处理代码
response = await Http.PostAsJsonAsync(Constants.uri, editModel); var res = response.Content.ReadAsStringAsync().Result; var validationResponse = JsonConvert.DeserializeObject<MymodelResponse>(res);
异常信息
blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: Unable to find a constructor to use for type System.ComponentModel.DataAnnotations.ValidationResult. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'validationResult.validationResults[0].memberNames', line 1, position 493. Newtonsoft.Json.JsonSerializationException: Unable to find a constructor to use for type System.ComponentModel.DataAnnotations.ValidationResult. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'validationResult.validationResults[0].memberNames', line 1, position 493. at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReader reader, JsonObjectContract objectContract, JsonProperty containerMember, JsonProperty containerProperty, String id, Boolean& createdFromNonDefaultCreator) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
模型定义
public class MymodelResponse : BaseResponse { public int MymodelId { get; set; } public string? Phone { get; set; } public long CreatedBy { get; set; } public DateTime CreatedDate { get; set; } public long? ModifiedBy { get; set; } public DateTime? ModifiedDate { get; set; } public bool IsDeleted { get; set; } public string? Notes { get; set; } public MymodelResponse(List<ValidationResult> validationResults) : base(validationResults) { } } // BaseResponse 片段 public class BaseResponse { public List<ValidationResult> ValidationResults { get; set; } }
解决方案
方案1:为ValidationResult添加自定义Json转换器
ValidationResult没有默认构造函数,Newtonsoft.Json无法自动反序列化,需自定义转换器处理:
public class ValidationResultConverter : JsonConverter<ValidationResult> { public override ValidationResult ReadJson(JsonReader reader, Type objectType, ValidationResult existingValue, bool hasExistingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var errorMessage = jsonObject["ErrorMessage"]?.ToString(); var memberNames = jsonObject["MemberNames"]?.ToObject<IEnumerable<string>>() ?? Enumerable.Empty<string>(); return new ValidationResult(errorMessage, memberNames); } public override void WriteJson(JsonWriter writer, ValidationResult value, JsonSerializer serializer) { var jsonObject = new JObject { ["ErrorMessage"] = value.ErrorMessage, ["MemberNames"] = JArray.FromObject(value.MemberNames) }; jsonObject.WriteTo(writer); } }
使用方式:
- 局部反序列化时注册:
var settings = new JsonSerializerSettings(); settings.Converters.Add(new ValidationResultConverter()); var validationResponse = JsonConvert.DeserializeObject<MymodelResponse>(res, settings);
- 全局配置时添加(服务注册阶段):
builder.Services.AddControllers().AddNewtonsoftJson(options => { options.SerializerSettings.Converters.Add(new ValidationResultConverter()); });
方案2:自定义DTO替代ValidationResult
创建一个与ValidationResult结构一致的DTO类,避免直接使用框架自带类型:
public class ValidationResultDto { public string? ErrorMessage { get; set; } public List<string> MemberNames { get; set; } = new List<string>(); public ValidationResult ToValidationResult() { return new ValidationResult(ErrorMessage, MemberNames); } }
修改BaseResponse与MymodelResponse:
public class BaseResponse { public List<ValidationResultDto> ValidationResults { get; set; } = new List<ValidationResultDto>(); public BaseResponse(List<ValidationResultDto> validationResults) { ValidationResults = validationResults; } public List<ValidationResult> GetValidationResults() { return ValidationResults.Select(dto => dto.ToValidationResult()).ToList(); } } public class MymodelResponse : BaseResponse { // 原有属性... public MymodelResponse(List<ValidationResultDto> validationResults) : base(validationResults) { } }
方案3:混合使用序列化库
保留AddNewtonsoftJson()支持Patch,前端处理验证响应时改用System.Text.Json(它可以处理无默认构造的类型):
response = await Http.PostAsJsonAsync(Constants.uri, editModel); var res = await response.Content.ReadAsStringAsync(); var validationResponse = System.Text.Json.JsonSerializer.Deserialize<MymodelResponse>(res);
若有特殊序列化需求,可配置System.Text.Json选项:
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, WriteIndented = true }; var validationResponse = System.Text.Json.JsonSerializer.Deserialize<MymodelResponse>(res, options);
内容的提问来源于stack exchange,提问作者Mayur Saner
相关产品推荐
相关产品推荐

