强制ASP.NET Core Controller序列化子类返回值时仅输出基类定义属性
问题原因
System.Text.Json 默认序列化策略为按照对象的运行时实际类型扫描可序列化属性,而非控制器方法定义的返回值声明类型,因此即使你声明返回Person,实际返回的DbPerson实例的所有公共属性都会被输出。
官方原生解决方案
以下方案均为.NET 官方原生支持,无需引入任何第三方库:
方案1:单接口显式指定序列化类型(适用少量接口场景)
直接在控制器动作中使用Json方法的重载,显式指定序列化时使用的声明类型即可,兼容 .NET Core 3.0 及以上版本:
public IActionResult Get() { Person person = repo.Get(); // 第二个参数指定序列化时使用的类型为基类Person return Json(person, typeof(Person), new JsonSerializerOptions(JsonSerializerDefaults.Web)); }
序列化器会完全按照Person类型的定义扫描属性,自动忽略子类DbPerson的额外属性,完全符合预期输出。
方案2:全局过滤器统一处理(适用全项目通用场景)
如果项目中大量接口都有类似需求,不需要逐个修改接口代码,只需添加一个全局结果过滤器即可:
- 定义过滤器
public class SerializeAsDeclaredTypeFilter : IResultFilter { public void OnResultExecuting(ResultExecutingContext context) { if (context.Result is ObjectResult objectResult && objectResult.DeclaredType != null && objectResult.Value != null && objectResult.Value.GetType() != objectResult.DeclaredType) { context.Result = new JsonResult( objectResult.Value, objectResult.DeclaredType, new JsonSerializerOptions(JsonSerializerDefaults.Web) ); } } public void OnResultExecuted(ResultExecutedContext context) { } }
- 全局注册过滤器
builder.Services.AddControllers(options => { options.Filters.Add<SerializeAsDeclaredTypeFilter>(); });
配置完成后所有控制器接口都会自动按照方法声明的返回类型序列化,无需修改任何现有业务代码。
方案3:子类属性标记为非公开(最简单轻量方案)
如果DbPerson的City、State、Zip属性仅在当前程序集内部访问使用,无需对外暴露,直接将其访问修饰符改为internal即可,System.Text.Json 默认不会序列化非公开属性,无需任何额外配置。
方案对比
| 方案 | 适用场景 | 改造成本 |
|---|---|---|
| 单接口指定类型 | 仅少量接口需要 | 极低 |
| 全局过滤器 | 全项目通用需求 | 中等,一次配置全局生效 |
| 非公开属性 | 子类属性无需对外访问 | 最低,仅修改类定义 |
内容的提问来源于stack exchange,提问作者brendonparker
相关产品推荐
相关产品推荐

