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

如何让System.Text.Json反序列化不可变DTO无需标注[JsonConstructor]

解决方案

方案1:自定义JsonTypeInfoResolver(推荐,适用于.NET 6+)

System.Text.Json 从.NET 6开始支持通过JsonTypeInfoResolver的修饰器动态配置类型的序列化/反序列化规则,不需要修改任何现有DTO代码,只需要全局配置序列化选项即可实现自动识别单公共构造函数的行为。

配置代码

using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

// 通用序列化选项配置
var jsonOptions = new JsonSerializerOptions
{
    // 配置属性名不区分大小写,和Newtonsoft.Json默认行为对齐
    PropertyNameCaseInsensitive = true,
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers = { AutoBindSinglePublicConstructor }
    }
};

// 构造函数自动绑定逻辑
static void AutoBindSinglePublicConstructor(JsonTypeInfo typeInfo)
{
    // 只处理普通对象类型,排除枚举、基础类型等
    if (typeInfo.Kind != JsonTypeInfoKind.Object)
        return;
    // 排除抽象类、接口
    if (typeInfo.Type.IsAbstract || typeInfo.Type.IsInterface)
        return;
    // 如果类型已经配置了创建逻辑,跳过
    if (typeInfo.CreateObject != null)
        return;
    
    // 取所有公共实例构造函数
    var publicConstructors = typeInfo.Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
    // 仅当只有一个公共构造函数时自动绑定
    if (publicConstructors.Length == 1)
    {
        typeInfo.ConstructorInfo = publicConstructors[0];
    }
}

ASP.NET Core 全局配置

如果是ASP.NET Core项目,直接在Program.cs中配置即可全局生效:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        options.JsonSerializerOptions.TypeInfoResolver = new DefaultJsonTypeInfoResolver
        {
            Modifiers = { AutoBindSinglePublicConstructor }
        };
    });

// 其余配置...

配置完成后,无[JsonConstructor]特性的Model结构体即可正常反序列化。

方案2:源生成器自动添加特性(适用于.NET Standard 2.0+ / 旧版本System.Text.Json)

如果你的项目版本低于.NET 6,无法使用JsonTypeInfoResolver,可以使用源生成器在编译阶段自动为所有符合条件的不可变类型添加[JsonConstructor]特性,不需要手动修改任何DTO代码:

  • 只需安装对应功能的源生成器NuGet包
  • 编译时会自动扫描所有只有单个公共构造函数的类/结构体,自动添加特性标记
  • 对现有代码零侵入,行为和手动加特性完全一致

注意事项

  • 构造函数的参数名需要和JSON属性名匹配,开启PropertyNameCaseInsensitive后支持大小写不敏感匹配,和Newtonsoft.Json行为一致
  • 如果类型存在多个公共构造函数,仍需要手动添加[JsonConstructor]指定要使用的构造函数,避免歧义

内容的提问来源于stack exchange,提问作者Jeremy Morren

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 04:15:03