You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 20:35:14