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.
- Add additional mapping methods. Mapstruct will use user-defined mapping methods over generating new ones.
- 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.
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
Passing List from Thymeleaf to Spring Controller
Error About Sun/Misc/Base64Encoder on Eclipse
Split Integer into Separate Parts
Mockito How to Mock Only the Call of a Method of the Superclass
Finding Max Value in an Array Using Recursion
How to Solve Liquibase Checksum Validation Fail After Liquibase Upgrade
Java.Lang.Noclassdeffounderror: Org/Json/Simple/Parser/Parseexception With Eclipse and Spring
Admob No Fill from Ad Server - Failed to Load Ad: 3
Check Date Between Two Other Dates Spring Data Jpa
Tomcat Is Running But 8080 Port Is Not Responding
What Is the Most Elegant Way to Check If All Values in a Boolean Array Are True
Wait Until All Threads Finish Their Work in Java
Set Current Timezone to @Jsonformat Timezone Value
How to Use Junit to Test Asynchronous Processes
Get Current Week Start and End Date in Java - (Monday to Sunday)
Java Sorting a 2D Array Rows into Ascending and Columns into Descending Order