mapstruct 将可迭代映射到不可迭代

Joh*_*lis 12 spring iterable mapstruct

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

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

public class Person {
    private String name;
}
Run Code Online (Sandbox Code Playgroud)

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

Group toGroup(List<Person> people);
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

error: Can't generate mapping method from iterable type to non-iterable type.
Run Code Online (Sandbox Code Playgroud)

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

Nil*_*oud 13

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);
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Myk*_*rol 8

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

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

/// 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
    }
}
Run Code Online (Sandbox Code Playgroud)

Group只有简单的构造函数并以列表作为参数时。您不需要为此使用 Mapstruct。在 Mapstruct 源中,检查MethodRetrievalProcessor.java 的 Mapstruct github 源

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

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

所以基本上,即使 Mapstruct 团队也希望您仅在需要时才使用映射。并且不想允许转变List<Object>为另一个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);
Run Code Online (Sandbox Code Playgroud)

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

  • @RamanujanR 请检查参数的顺序。仅当您具有不可迭代的第一个参数时,才可以使用不可迭代的目标,反之亦然。另外 - 请注意,在我的示例中,“List&lt;Person&gt;”是“Group”中的一个字段。如果您尝试在输入中传递“List&lt;Person&gt;”,但期望将单个“Person”作为“Group”的字段,则它将不起作用 - 为此,您需要指定如何将可迭代映射到不可迭代(例如,将默认方法放置在映射器类中,并在其中包含所需的签名和实际映射) (2认同)