MapStruct: 如何将所有属性映射到列表的第一个元素?

8
我需要一个映射来实现这个目标:
@Mapping(source = "a", target = "result.transactions[0].a"),
@Mapping(source = "b", target = "result.transactions[0].b"),
@Mapping(source = "c", target = "result.transactions[0].c"),
...
Response dataToResponse(DataModel model);

但是这种语法不起作用(顺便说一下:使用Spring Bean包装器可以工作)。 像这样的解决方案只是一个半成品解决方案。
@AsList
public <T> List<T> asList( T in ) {
    List<T> result = new ArrayList<T>();
    if ( in!=null ) {
       result.add(in);
   }
   return result;
}

这仅适用于正好一个属性,因为它始终为每个属性创建一个新列表。我不需要将每个属性映射到新列表的第一个元素。该列表必须被重复使用,但我不知道如何实现。有什么正确的方法来完成这个任务呢? 我考虑了以下内容:

@Mapping(source = "a", target = "transaction.a"),
@Mapping(source = "b", target = "transaction.b"),
@Mapping(source = "c", target = "transaction.c"),
...
Transaction dataToTransaction(DataModel model);

然后...

@Mapping([use Transaction from b4], target = "result");

但是我如何传递上面已经映射好的字段呢? (我使用的是最新的1.1.0.Final版本)
3个回答

6
我认为在这里使用表达式是一种解决方法:
public interface MyMapper {
    @Mapping(target = "subjectName", expression = "java(source.getCourses().get(0).getCourseName())")
    Target map(Source source);
} 

此外,更多信息可以在此处查看:https://github.com/mapstruct/mapstruct/issues/1321

我在我的代码中像这样实现:

@Mapping(expression = "java(player.getAddressBooks().get(0).getPostCode())", target = "postCode")
@Mapping(expression = "java(player.getAddressBooks().get(0).getCity())", target = "city")
@Mapping(expression = "java(player.getAddressBooks().get(0).getStreetNumber())", target = "streetNumber")
@Mapping(expression = "java(player.getAddressBooks().get(0).getStreetName())", target = "streetName")
@Mapping(expression = "java(player.getAddressBooks().get(0).getCountryISO())", target = "countryISO")

1
它会起作用,但是对于中间列表为空的验证并不是由MapStruct生成的,这意味着如果AddressBooks为空,它仍然容易受到NPE的影响。 - Siba Prasad Tripathy
@SibaPrasadTripathy 你找到解决办法了吗? - Abdullah Imran
很遗憾,我最终在我的项目中使用了很多Objects.nonNull检查。 - undefined

3
很明显这个问题没有干净的解决方案。所以我必须通过将以下映射排除到单独的映射器中来解决它。
@Mapping(source = "a", target = "transaction.a"),
@Mapping(source = "b", target = "transaction.b"),
@Mapping(source = "c", target = "transaction.c"),
Transaction dataToTransaction(DataModel model);

在主映射器中,我通过表达式执行单独的映射器,并将其转换为列表:
@Mapping(expression = "java(Arrays.asList(SubMapper.INSTANCE.dataToTransaction(model)))", target = "result.transactions")

我是mapstruct的新手,正在寻找相同的解决方案,您能否提供更多的信息,以便我能够理解它。 - Nishit Charania

2

我刚看到这个问题。更优雅的解决方案应该是这样的:

@Mapping(source = "a", target = "transaction.a"),
@Mapping(source = "b", target = "transaction.b"),
@Mapping(source = "c", target = "transaction.c"),
Transaction dataToTransaction(DataModel model);
  • 这与之前的答案相同

剩下要做的就是将单个事务映射到一个元素列表:

default List<Transaction> mapTransactionToList(Transaction source) {
     return ImmutableList.of(source);
}

现在您可以在@Mapping定义中将model简单地映射到List<Transaction>,MapStruct会自动处理。我认为这比基于“表达式”的解决方案更好看,而且出错的可能性更小。请注意,您可以将代码包含在Mapper接口中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接