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

如何通过ModelMapper的addMapping实现RoomType到RoomTypeDto的描述字段自定义映射?

解决ModelMapper中Optional嵌套字段的自定义映射问题

问题背景

在项目中使用ModelMapper进行DTO与实体映射时,尝试为RoomTypeDto定义自定义映射:将RoomType中Optional<Description>类型的description字段,提取其内部的description字符串赋值给RoomTypeDto的同名字段,过程中遇到异常。

错误代码及异常信息

错误代码

var modelMapper = new ModelMapper();
modelMapper.getConfiguration()
        .setMatchingStrategy(MatchingStrategies.STRICT)
        .setPreferNestedProperties(false);
modelMapper.createTypeMap(RoomType.class, RoomTypeDto.class)
        .addMapping(rt -> rt.getDescription().map(Description::getDescription).orElse(null), RoomTypeDto::setDescription);

抛出的异常

Caused by: org.modelmapper.internal.ErrorsException: null
at org.modelmapper.internal.Errors.toException(Errors.java:253)
at org.modelmapper.internal.ReferenceMapExpressionImpl.visitSource(ReferenceMapExpressionImpl.java:113)
at org.modelmapper.internal.ReferenceMapExpressionImpl.map(ReferenceMapExpressionImpl.java:66)
at org.modelmapper.internal.TypeMapImpl.addMapping(TypeMapImpl.java:260)
at pl.ekoncept.room.mapper.RoomTypeReadMapper.<init>(RoomTypeReadMapper.java:20)

可正常运行的跳过代码

modelMapper.createTypeMap(RoomType.class, RoomTypeDto.class)
          .addMappings(m -> m.skip(RoomTypeDto::setDescription));

实体与DTO结构

RoomTypeDto

@Data
@NoArgsConstructor
public class RoomTypeDto {
    private String description;
    [...]
}

RoomType实体

@Entity
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
public class RoomType {
    private Description description;
    [...]
    public Optional<Description> getDescription() {
        return Optional.ofNullable(description);
    }
    [...]
}

Description实体

@Entity
@Getter
@Setter
@EqualsAndHashCode(of = "id")
@NoArgsConstructor
public class Description {
    private String description;
    [...]
}

解决方案

问题根源在于:ModelMapper的addMapping方法要求源表达式是可被解析的属性引用路径,无法直接识别Lambda中的Optional流式操作(map/orElse),会尝试解析该表达式的属性结构导致异常。

可以通过以下两种方式实现正确映射:

方式一:使用Converter转换类型

定义一个Converter处理Optional<Description>到String的转换,再绑定到映射中:

// 定义转换器
Converter<Optional<Description>, String> descriptionConverter = context -> 
    context.getSource().map(Description::getDescription).orElse(null);

// 绑定转换器到映射
modelMapper.typeMap(RoomType.class, RoomTypeDto.class)
    .addMappings(mapper -> mapper.using(descriptionConverter)
        .map(RoomType::getDescription, RoomTypeDto::setDescription));

方式二:使用PostConverter后置处理

通过setPostConverter在整体转换完成后,手动处理description字段的赋值:

modelMapper.typeMap(RoomType.class, RoomTypeDto.class)
    .setPostConverter(context -> {
        RoomType source = context.getSource();
        RoomTypeDto destination = context.getDestination();
        // 手动处理Optional字段的提取
        destination.setDescription(source.getDescription().map(Description::getDescription).orElse(null));
        return destination;
    });

这两种方式都能绕过ModelMapper对属性路径的解析,直接执行自定义的映射逻辑,完成从RoomType.description.description到RoomTypeDto.description的赋值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:42:03