MapStruct报错:MetaData中firstProperty无写入访问器的解决咨询
MapStruct映射嵌套不可变Kotlin数据类报错解决方案
问题背景
需要将自定义FlatDTO映射到OpenAPI自动生成的嵌套Response类(生成代码不可修改),执行mvn clean install时出现如下错误:
error: Property "firstProperty" has no write accessor in MetaData for target name "metaData.firstProperty". [ERROR] @org.mapstruct.Mappings(value = {@org.mapstruct.Mapping(target = "infoData.id", source = "id"), @org.mapstruct.Mapping(target = "metaData.firstProperty", source = "firstProperty")})
原因是OpenAPI生成的MetaData类中firstProperty为val修饰的不可变属性,没有setter方法,默认MapStruct会尝试通过setter赋值导致报错。
相关代码
OpenAPI生成的Response类
package org.mapstruct.example.kotlin.openapi import com.fasterxml.jackson.annotation.JsonProperty import javax.validation.Valid data class Response( @field:Valid @field:JsonProperty("infoData", required = true) val infoData: InfoData, @field:Valid @field:JsonProperty("metaData", required = true) val metaData: MetaData ) data class InfoData( @field:JsonProperty("id", required = true) val id: kotlin.String, ) data class MetaData( @field:JsonProperty("firstProperty") val firstProperty: String? = null, )
自定义FlatDTO类
package org.mapstruct.example.kotlin.models data class FlatDTO( var id: String? = null, var firstProperty: String, )
原MapStruct映射器
package org.mapstruct.example.kotlin.mapper import org.mapstruct.Mapper import org.mapstruct.Mapping import org.mapstruct.Mappings import org.mapstruct.example.kotlin.models.FlatDTO import org.mapstruct.example.kotlin.openapi.Response @Mapper interface DataMapper { @Mappings( Mapping(target = "infoData.id", source = "id"), Mapping(target = "metaData.firstProperty", source = "firstProperty") ) fun flatToResponse(flatDTO: FlatDTO): Response }
解决方案
方法1:显式调用嵌套类构造器
利用Kotlin数据类的构造器初始化嵌套对象,绕过setter需求。修改映射器如下:
@Mapper interface DataMapper { @Mappings( Mapping(target = "infoData", expression = "java(new org.mapstruct.example.kotlin.openapi.InfoData(flatDTO.getId()))"), Mapping(target = "metaData", expression = "java(new org.mapstruct.example.kotlin.openapi.MetaData(flatDTO.getFirstProperty()))") ) fun flatToResponse(flatDTO: FlatDTO): Response }
或者用更符合Kotlin风格的自定义方法:
import org.mapstruct.Named import org.mapstruct.Mapper import org.mapstruct.Mapping @Mapper interface DataMapper { @Mapping(target = "infoData", source = ".", qualifiedByName = ["toInfoData"]) @Mapping(target = "metaData", source = ".", qualifiedByName = ["toMetaData"]) fun flatToResponse(flatDTO: FlatDTO): Response @Named("toInfoData") fun toInfoData(flatDTO: FlatDTO): InfoData { return InfoData(flatDTO.id ?: throw IllegalArgumentException("id cannot be null")) } @Named("toMetaData") fun toMetaData(flatDTO: FlatDTO): MetaData { return MetaData(flatDTO.firstProperty) } }
方法2:启用构造器优先映射模式
在映射器上配置componentModel并指定构造器注入策略,让MapStruct优先使用构造器而非setter:
import org.mapstruct.Mapper import org.mapstruct.Mapping import org.mapstruct.Mappings import org.mapstruct.InjectionStrategy @Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR) interface DataMapper { @Mappings( Mapping(target = "infoData.id", source = "id"), Mapping(target = "metaData.firstProperty", source = "firstProperty") ) fun flatToResponse(flatDTO: FlatDTO): Response }
注意:此方法要求MapStruct版本≥1.4.0,确保能正确识别Kotlin数据类的构造器参数。
核心原理
Kotlin数据类的val属性虽无setter,但会生成包含所有属性的主构造器。MapStruct支持通过构造器初始化对象,只要显式指定构造调用或启用构造器映射模式,就能避开无setter的限制。
内容的提问来源于stack exchange,提问作者Sam Si
相关产品推荐
相关产品推荐

