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

如何通过Jackson根据DTO使用场景排除嵌套属性,避免定义重复DTO类?

如何通过Jackson根据DTO使用场景排除嵌套属性,避免定义重复DTO类?

兄弟,我太懂你这种痛点了——为了不同的序列化场景定义一堆几乎重复的DTO类,不仅代码冗余,维护起来也闹心。好在Jackson有好几招能帮你解决这个问题,不用再搞那些“NotNested”后缀的重复类了,我给你唠几个实用的方案:

一、最直观的方案:用@JsonView做场景化序列化

这是我最推荐的方案,代码侵入性低,逻辑清晰,完全适配你这种“同一个DTO在不同场景下序列化不同字段”的需求。

步骤1:定义序列化视图

先搞几个空接口当标记,用来区分不同的序列化场景:

// 序列化CountryDTO时用这个视图
interface CountrySerializationView
// 单独序列化SubContinentDTO时用这个视图
interface SubContinentFullView

步骤2:给目标字段绑定视图

修改你的SubContinentDTO,把continent字段绑定到SubContinentFullView上——意思就是只有在使用这个视图序列化时,这个字段才会被输出:

data class SubContinentDTO(
    val name: String,
    @JsonView(SubContinentFullView::class)
    val continent: ContinentDto
)

你的CountryDTO和ContinentDTO保持原样就行,不用改:

data class CountryDTO(
    val name: String,
    val isoAlpha3: String,
    val subContinent: SubContinentDTO,
    val continent: ContinentDto
)

data class ContinentDTO(val name: String)

步骤3:序列化时指定视图

  • 当你要返回CountryDTO时,指定用CountrySerializationView,这样SubContinentDTO里的continent字段就会被自动忽略:
val mapper = ObjectMapper().registerModule(KotlinModule())
// 指定视图
val writer = mapper.writerWithView(CountrySerializationView::class.java)
val json = writer.writeValueAsString(yourCountryDto)

输出的JSON里,subContinent只会有name字段,不会带重复的continent。

  • 当你单独返回SubContinentDTO时,指定用SubContinentFullView,就能正常输出continent字段:
val writer = mapper.writerWithView(SubContinentFullView::class.java)
val json = writer.writeValueAsString(yourSubContinentDto)

如果是Spring Boot项目,更简单——直接在控制器方法上加@JsonView注解就行,不用手动搞ObjectWriter:

@GetMapping("/country/{id}")
@JsonView(CountrySerializationView::class)
fun getCountry(@PathVariable id: String): CountryDTO {
    // 业务逻辑返回CountryDTO
}

@GetMapping("/subcontinent/{id}")
@JsonView(SubContinentFullView::class)
fun getSubContinent(@PathVariable id: String): SubContinentDTO {
    // 业务逻辑返回SubContinentDTO
}

二、更灵活的方案:用@JsonFilter做动态字段过滤

如果你的场景更复杂,比如需要根据业务逻辑动态决定要不要过滤字段,那@JsonFilter就更适合你。

步骤1:给DTO标记过滤器

给SubContinentDTO加上@JsonFilter注解,指定一个过滤器名称:

@JsonFilter("subContinentFieldFilter")
data class SubContinentDTO(
    val name: String,
    val continent: ContinentDto
)

步骤2:序列化时动态配置过滤器

  • 当序列化CountryDTO时,配置过滤器排除continent字段:
val mapper = ObjectMapper().registerModule(KotlinModule())
// 创建过滤器,指定要排除的字段
val filterProvider = SimpleFilterProvider().addFilter(
    "subContinentFieldFilter",
    SimpleBeanPropertyFilter.serializeAllExcept("continent")
)
// 绑定过滤器到ObjectWriter
val writer = mapper.writer(filterProvider)
val json = writer.writeValueAsString(yourCountryDto)
  • 当单独序列化SubContinentDTO时,直接用默认的序列化逻辑(或者不设置过滤器,会输出所有字段):
val json = mapper.writeValueAsString(yourSubContinentDto)

这个方案的好处是,你可以在运行时动态调整要过滤的字段,比如根据用户权限、请求参数来决定要不要输出某些字段,灵活性拉满。

三、不推荐的方案:自定义序列化器

当然你也可以写自定义的JsonSerializer来手动控制序列化逻辑,但这种方式代码量太大,维护起来麻烦,除非你有非常特殊的需求,否则不建议用——毕竟前面两种方案已经能解决90%的场景了。

总结

优先用@JsonView,简单直观,代码改动最小;如果需要动态调整过滤规则,再上@JsonFilter。这样你就不用再定义那些冗余的“NotNested”DTO类了,代码清爽多了!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:17:56