Mapstruct: Map List of Objects, When Object Is Mapped from Two Objects

Mapstruct map list to an object containing list

Mapstruct does not support such kind mapping. See Mapstruct contributor answer regarding this problem.

Like workaround you can implement your own mapping method for converting to wrapped class.

@org.mapstruct.Mapper
public interface Mapper {
List<Person> map(List<Employee> employees);

Person map(Employee employee);

default Group mapGroup(List<Employee> employees) {
Group group = new Group();
group.people = map(employees);
return group;
}
}

Mapstruct - Mapping an object's list item with a method in a different mapper

this is possible with the uses directive.

for example:

@Mapper(uses=CountryLanguageMapper.class)
public interface CountryMapper {

for more information see here:
https://mapstruct.org/documentation/dev/reference/html/#invoking-other-mappers

Mapstruct : How do I map hierarchical structure where the child object is a list and have attributes which are lists

There are 2 things that you want to do here.

  1. Add additional mapping methods. Mapstruct will use user-defined mapping methods over generating new ones.
  2. Use @AfterMapping to pass on the generated GUID's.
// custom mapping annotations
TopLevelDomain toTopLevel( TopLevel topLevel );

// custom mapping annotations
SecondLevelDomain toSecondLevel( TopLevel.SecondLevel secondLevel );

// custom mapping annotations
ThirdLevelDomain toThirdLevel( TopLevel.SecondLevel.ThirdLevel thirdLevel );

// custom mapping methods

@AfterMapping
default void handleGuidTransfer( TopLevelDomain topLevel ){
for ( SecondLevelDomain secondLevel: topLevel.getSecondLevelDomain() ) {
secondLevel.setGuid1( topLevel.getGuid1() );
for ( ThirdLevelDomain thirdLevel: secondLevel.getThirdLevelDomain() ) {
thirdLevel.setGuid1( topLevel.getGuid1() );
thirdLevel.setGuid2( secondLevel.getGuid2() );
}
}
}

MapStruct: Map multiple sources from multiple objects to one target

This can be done in several ways. Below you see 2 options. They do the same thing only one uses qualifiedByName while the other uses expression. Depending on your need one might fit better then the other.

Using a custom method found by mapstruct

qualifiedByName required because otherwise mapstruct does not know which method to use.

    @Mapping(source = ".", target = "startTime", qualifiedByName = "startTime")
@Mapping(source = ".", target = "endTime", qualifiedByName = "endTime")
AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule availabilityRule, @Context Schedule schedule);

@Named("startTime")
protected ZonedDateTime startTime(AvailabilityRule availabilityRule, @Context Schedule schedule) {
return convertTime(availabilityRule.startEpoch, schedule.timezoneId);
}

@Named("endTime")
protected ZonedDateTime endTime(AvailabilityRule availabilityRule, @Context Schedule schedule) {
return convertTime(availabilityRule.endEpoch, schedule.timezoneId);
}

private ZonedDateTime convertTime(long epoch, String timezoneId) {
Instant instant = Instant.ofEpochMilli(epoch);
ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
return time;
}

Using a custom method configured by an expression

@Named used here to prevent mapstruct from accidentally using this method for other mapping actions. Without it it will most likely still work.

    @Mapping(target = "startTime", expression = "java(convertTime(availabilityRule.startEpoch, schedule.timezoneId))" )
@Mapping(target = "endTime", expression = "java(convertTime(availabilityRule.endEpoch, schedule.timezoneId))" )
AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
availabilityRule, Schedule schedule);


@Named("time")
protected ZonedDateTime convertTime(long epoch, String timezoneId) {
Instant instant = Instant.ofEpochMilli(epoch);
ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
return time;
}
Complete mapper including schedule

@Mapper
public abstract class ScheduleMapper {
@Mapping(target = "timezone", source = "timezoneId")
@Mapping(target = "rules", expression = "java(toAvailabilityRuleDTOs(schedule.getRules(), schedule))")
abstract ScheduleDTO toScheduleDTO(Schedule schedule);

@Named("rules")
abstract List<AvailabilityRuleDTO> toAvailabilityRuleDTOs(List<AvailabilityRule> rules, @Context Schedule schedule);

@Mapping(target = "startTime", expression = "java(convertTime(availabilityRule.startEpoch, schedule.timezoneId))")
@Mapping(target = "endTime", expression = "java(convertTime(availabilityRule.endEpoch, schedule.timezoneId))")
abstract AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule availabilityRule, @Context Schedule schedule);

@Named("time")
protected ZonedDateTime convertTime(long epoch, ZoneId timezoneId) {
Instant instant = Instant.ofEpochMilli(epoch);
ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
return time;
}

String map(ZoneId zoneId) {
return zoneId == null ? null : zoneId.getId();
}
}

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

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;
}

MapStruct - Mapping two lists with different object types to a third list with another object type

This cannot be done by mapstruct. There's always business logic involved in merging 2 list. So you need b to write your own method implementation. I recently wrote a FAQ item on the MapStruct web page. This applies to a pre existing list @MappingTarget. But similar reasoning applies to merging 2 source lists into one target. http://mapstruct.org/faq/#why-is-it-not-possible-for-mapstruct-to-generate-implementations-for-iterable-stream-and-map-types-from-update-mappingtarget-methods

How to use multiple properties from an object and map it to a single one in an other?

Solution 1: Method qualifier

@Mapper
public interface MyMapper {

@Mappings({
@Mapping(target="name", source="bObject.nameB"),
@Mapping(target="firstname", source="bObject.firstnameB"),
@Mapping(target="id", source="bObject", qualifiedByName="createId")
})
A BToA(B bObject);

@Named("createId")
default String createId(B bObject) {
return bObject.nameB + bObject.firstnameB;
}
}

Generated code:

public class MyMapperImpl implements MyMapper {
@Override
public A BToA(B bObject) {
if ( bObject == null ) {
return null;
}
A a = new A();
a.name = bObject.nameB;
a.firstname = bObject.firstnameB;
a.id = createId( bObject );
return a;
}
}

Solution 2: Expression

@Mapper
public interface MyMapper {
@Mappings({
@Mapping(target="name", source="bObject.nameB"),
@Mapping(target="firstname", source="bObject.firstnameB"),
@Mapping(target="id", expression = "java(bObject.nameB + bObject.firstnameB)")
})
A BToA(B bObject);
}

Generated code:

public class MyMapperImpl implements MyMapper {
@Override
public A BToA(B bObject) {
if ( bObject == null ) {
return null;
}
A a = new A();
a.name = bObject.nameB;
a.firstname = bObject.firstnameB;
a.id = bObject.nameB + bObject.firstnameB;
return a;
}
}

Solution 3: AfterMapping annotation

@Mapper
public interface MyMapper {
@Mappings({
@Mapping(target="name", source="bObject.nameB"),
@Mapping(target="firstname", source="bObject.firstnameB"),
})
A BToA(B bObject);

@AfterMapping
default void afterMapping(@MappingTarget A aObject, B bObject) {
aObject.id = bObject.nameB + bObject.firstnameB;
}
}

Generated code:

public class MyMapperImpl implements MyMapper {
@Override
public A BToA(B bObject) {
if ( bObject == null ) {
return null;
}
A a = new A();
a.name = bObject.nameB;
a.firstname = bObject.firstnameB;
afterMapping( a, bObject );
return a;
}
}


Related Topics



Leave a reply



Submit