Mapstruct: Mapping a List of Objects into two lists of Strings/UUIDs

Viewed 3467

I need a mapstruct mapping for a class that has a List of objects to be mapped into the target class this way:

//Source class:
public class VoucherTransaction {
    private List<Voucher> vouchers;
}

//TargetClass
public class VoucherTransactionServiceDTO {
    private List<UUID> voucherIds;
    private List<String> voucherSerials;
}

public class Voucher {
    private UUID id;
    private String serial;
}
2 Answers

If you define methods that can do a mapping between different list types or the objects of the list with other objects mapstruct will recognize them and do the mapping.

e.g.

@Mapper
public interface VoucherTransactionMapper {

    @Mapping(target = "voucherIds", source = "vouchers")
    @Mapping(target = "voucherSerials", source = "vouchers")
    public VoucherTransactionServiceDTO map(VoucherTransaction transaction);

    default UUID voucherToUuid(Voucher voucher) {
        return voucher != null ? voucher.getId() : null;
    }


    default String voucherToSerial(Voucher voucher) {
        return voucher != null ? voucher.getSerial() : null;
    }

}

MapStruct will then generate mappings between List<Voucher> and List<UUID> and List<Voucher> and List<String> and use the default methods defined in the mapper to perform the mapping.

You can also define list mapping methods and MapStruct will use them instead:

e.g.

@Mapper
public interface VoucherTransactionMapper {

    @Mapping(target = "voucherIds", source = "vouchers")
    @Mapping(target = "voucherSerials", source = "vouchers")
    public VoucherTransactionServiceDTO map(VoucherTransaction transaction);

    default List<UUID> vouchersToUuids(List<Voucher> vouchers) {
        if (vouchers == null) {
            return null;
        }

        return vouchers.stream().map(Voucher::getId).collect(Collectors.toList());
    }


    default List<UUID> vouchersToSerials(List<Voucher> vouchers) {
        if (vouchers == null) {
            return null;
        }

        return vouchers.stream().map(Voucher::getSerial).collect(Collectors.toList());
    }

}

For more advanced usages you can use Mapping method selection based on qualifiers

In your mapper class, you could use the expression for each target, and implement a separate default mapping for each list in your target.

@Mapping(target = "voucherIds", expression = "java( mapVoucherListToVoucherIdList(transaction.getVouchers()) )")
@Mapping(target = "voucherSerials", expression = "java( mapVoucherListToVoucherSerialList(transaction.getVouchers()) )")
public VoucherTransactionServiceDTO TransactionToServiceDTO(VoucherTransaction transaction);

default List<UUID> mapVoucherListToVoucherIdList(List<Voucher> vouchers) {
    List<UUID> voucherIds = new ArrayList<>();
    if (vouchers != null && !vouchers.isEmpty())
        voucherIds = vouchers.stream().map(Voucher::getId).collect(Collectors.toList());
    return voucherIds;
}

default List<String> mapVoucherListToVoucherSerialList(List<Voucher> vouchers) {
    List<String> voucherSerials = new ArrayList<>();
    if (vouchers != null && !vouchers.isEmpty())
        voucherSerials = vouchers.stream().map(Voucher::getSerial).collect(Collectors.toList());
    return voucherSerials;
}
Related