【问题标题】:mapstruct map iterable to non iterablemapstruct 将可迭代映射到不可迭代
【发布时间】:2019-08-15 21:32:37
【问题描述】:

我想将一个对象列表映射到一个包含列表的对象:

public class Group {
    private List<Person> people;
}

public class Person {
    private String name;
}

我尝试创建这样的映射器:

Group toGroup(List<Person> people);

我收到了这个错误:

error: Can't generate mapping method from iterable type to non-iterable type.

这种映射最优雅的解决方案是什么?

【问题讨论】:

标签: spring iterable mapstruct


【解决方案1】:

一般回答 - 禁止此类映射。

要将对象列表映射到包装该列表的对象,可以通过以下方式完成:

/// your class with business code
List<Person> people = ....
new Group(people);

/// group class
public class Group {
    private List<Person> people = new ArrayList<>();
    public Group(List<Person> people) {
       this.people = people
    }
}

Group 将只有一个简单的构造函数,并带有一个列表作为参数。您不需要为此使用 Mapstruct。 在mapstruct源中有这个检查Mapstruct github sources for MethodRetrievalProcessor.java:

        Type parameterType = sourceParameters.get( 0 ).getType();

        if ( parameterType.isIterableOrStreamType() && !resultType.isIterableOrStreamType() ) {
            messager.printMessage( method, Message.RETRIEVAL_ITERABLE_TO_NON_ITERABLE );
            return false;
        }

所以基本上,即使是 Mapstruct 团队也希望您仅在需要时使用映射。并且不想允许将List&lt;Object&gt; 转换为另一个Object,因为它没有意义。 如果您要为 Group 对象添加一些附加信息(不可迭代:)),这将是有意义的,例如:

//group class
public class Group {
    private Long someCounter;
    private List<Person> people;
}

//mapper
@Mapping(target= "people", source ="people")
@Mapping(target= "someCounter", source ="counterValue")
Group toGroup(Long counterValue, List<Person> people);

但最好使用 DTO、视图、实体和任何其他可以隐藏所有嵌套内容的对象。在这种情况下,Mapstruct 将是您最好的朋友。

【讨论】:

  • 你给Group toGroup(Long counterValue, List&lt;Person&gt; people);的这个方法对我不起作用。无法从可迭代生成到不可迭代。
  • @RamanujanR 请检查您的参数的顺序。只有当您具有不可迭代的第一个参数时,您才能使用不可迭代的目标,反之亦然。另外 - 请注意,在我的示例中,List&lt;Person&gt;Group 中的一个字段。如果您尝试在输入中传递List&lt;Person&gt;,但希望将单个Person 作为Group 的字段,它将不起作用 - 为此您需要指定如何将可迭代的映射到不可迭代的(例如,在映射器类中放置默认方法,并在其中包含所需的签名和实际映射)
  • 你是对的@mykola-karol。这有效:Group toGroup(Long counterValue, List&lt;Person&gt; people);。这不会Group toGroup(List&lt;Person&gt; people, Long counterValue);。排序很重要,我想知道为什么 MapStruct 限制了第一个排序而不是第二个!!
  • @RamanujanR,太棒了! :) 现在你可以投票给我的帖子了 :D P.S.我已经在 Mapstruct 中发布了来自 MethodRetrievalProcessor.java 的代码。他们只是得到第一个项目,我想他们这样做是因为如果他们试图动态地这样做 - 这将导致难以跟踪/意外的行为,所以他们尽可能简单(更不用说实际上从所有对象构建关系图,有时可以在您没想到的对象之间进行 5-6 层映射:D)
【解决方案2】:

Mapstruct 实际上可以做到。我有完全相同的情况,一个 CollectionResponse 只包含项目。我通过添加这样的虚拟参数来解决它:

@Mapper(componentModel = "spring", uses = ItemMapper.class)
public interface CollectionResponseMapper {
   // Dummy property to prevent Mapstruct complaining "Can't generate mapping method from iterable type to non-iterable type."
   @Mapping( target = "items", source = "items")
   CollectionResponse map( Integer dummy, List<Item> items);
}

Mapstruct 生成所需的代码。像这样的:

public class CollectionResponseMapperImpl implements CollectionResponseMapper {

    @Autowired
    private ItemMapper itemMapper;

    @Override
    public CollectionResponse map(Integer dummy, List<Item> items) {
        if ( dummy == null && items == null ) {
            return null;
        }

        CollectionResponse collectionResponse = new CollectionResponse();

        if ( items != null ) {
            collectionResponse.setItems( itemListToItemDtoList( items ) );
        }

        return collectionResponse;
    }

    protected List<ItemDto> itemListToItemDtoList(List<Item> list) {
        if ( list == null ) {
            return null;
        }

        List<ItemDto> list1 = new ArrayList<ItemDto>( list.size() );
        for ( Item item : list ) {
            list1.add( itemMapper.mapItemToDto( item ) );
        }

        return list1;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-23
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 2013-03-23
    相关资源
    最近更新 更多