如何让MapStruct从源对象提取值并返回为目标值?
MapStruct 对象转单个字段值的映射问题
我在用MapStruct时遇到一个问题:MapStruct可以将普通值映射到目标对象的字段中(比如把String转成EmailData对象),但反向操作——从源对象提取单个字段并返回对应类型(比如从EmailData提取email字段返回String)——当前写法无法实现预期效果。
代码示例
1. Mapper接口
import com.example.pixel_user_api.data.entity.EmailData; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @Mapper public interface EmailMapper { @Mapping(source = "email", target = ".") String toString(EmailData emailData); // 不生效的方法 @Mapping(source = ".", target = "email") EmailData toEmailData(String email); // 正常工作的方法 }
2. 实体类EmailData
import lombok.Getter; import lombok.Setter; @Getter @Setter public class EmailData { private Long id; private String email; }
MapStruct生成的实现代码
@Override public String toString(EmailData emailData) { if ( emailData == null ) { return null; } String string = new String(); return string; } @Override public EmailData toEmailData(String email) { if ( email == null ) { return null; } EmailData emailData = new EmailData(); emailData.setEmail( email ); return emailData; }
可以看到,toString方法只是返回了一个空字符串,没有正确提取emailData的email字段值。
测试代码及失败结果
测试代码
import com.example.pixel_user_api.mapper.EmailMapper; import org.junit.jupiter.api.Test; import org.mapstruct.factory.Mappers; import static org.assertj.core.api.Assertions.assertThat; class EmailDataTest { @Test void testMapper() { String expectedEmail = "example@mail.com"; EmailData emailData = new EmailData(); emailData.setEmail(expectedEmail); EmailMapper mapper = Mappers.getMapper(EmailMapper.class); String actualEmail = mapper.toString(emailData); assertThat(actualEmail).isEqualTo(expectedEmail); // 此处断言失败 } }
失败结果
org.opentest4j.AssertionFailedError: expected: "example@mail.com" but was: "" Expected :"example@mail.com" Actual :""
虽然手动实现toString(EmailData)很简单(直接return emailData.getEmail()),但我想知道能否通过MapStruct自动实现这个逻辑?
解决方案
方式1:直接定义方法,无需@Mapping注解
MapStruct会自动识别源对象的getter方法,当方法返回简单类型时,会自动匹配源对象中同类型的属性(如果只有一个同类型属性)。修改Mapper接口如下:
@Mapper public interface EmailMapper { String toString(EmailData emailData); // 自动映射emailData.getEmail() @Mapping(source = ".", target = "email") EmailData toEmailData(String email); }
此时MapStruct生成的toString方法会正确提取email字段:
@Override public String toString(EmailData emailData) { if ( emailData == null ) { return null; } String string = emailData.getEmail(); return string; }
方式2:使用@Mapping的expression属性(适用于复杂场景)
如果需要添加额外逻辑(比如空值处理、字段转换等),可以用expression属性直接指定代码逻辑:
@Mapper public interface EmailMapper { @Mapping(target = ".", expression = "java(emailData != null ? emailData.getEmail() : \"default@example.com\")") String toString(EmailData emailData); @Mapping(source = ".", target = "email") EmailData toEmailData(String email); }
这种方式会将表达式中的Java代码直接插入到生成的实现中,灵活性更高。
注意事项
- 不要使用
@Mapping(source = "email", target = ".")这种写法,MapStruct无法正确解析目标为"."的简单类型映射,这是导致原代码失效的原因。 - 如果源对象中有多个同类型的属性,需要明确指定要提取的字段,此时可以结合
@Named注解和@Mapping的qualifiedByName属性来区分。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

