11

I want to map the following classes

class Schedule {
    ZoneId timezoneId;
    List<AvailabilityRule> rules;
}
class AvailabilityRule {
    long startEpoch;
    long endEpoch;
}

to these classes.

class ScheduleDTO {
    String timezone;
    List<AvailabilityRuleDTO> rules;
}
class AvailabilityRuleDTO {
    ZonedDateTime startTime;
    ZonedDateTime endTime;
}

Both timezoneId and startEpoch are needed for calculating startTime.

Instant instant = Instant.ofEpochMilli(startEpoch);
ZonedDateTime zonedDateTime = instant.atZone(timezoneId);
        

How can I achieve this using mapstruct?

Pseudo code of what I want

@Mapping(source = {"startEpoch", "timezoneId"}, target = "startTime", qualifiedByName = "epochToString")
AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
                                            availabilityRule, Schedule schedule);

2 Answers 2

21

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();
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

The code you've mentioned is converting AvailabilityRule to AvailabilityRuleDTO properly. How do I convert Schedule to Schedule DTO? ``` @Mapping(source = "timeZone", target = "timeZone", qualifiedByName = "zoneIdToString") ScheduleDTO toScheduleDTO(Schedule schedule); ``` isn't working fine.
I'm not able to use toAvailabilityRuleDTO in conjunction with toScheduleDTO. My goal is to convert Schedule to ScheduleDTO.
I've added an example that contains the complete mapper that also maps schedule.
Thanks. This is what I'm looking for. Is there any way expression = "java(toAvailabilityRuleDTOs(schedule.getRules(), schedule))" can be avoided and replaced with qualifiedByName ?
I do not think that is possible at the moment, but create an issue at github.com/mapstruct/mapstruct/issues about it. Perhaps it is possible and I do not know about it, or otherwise it might be possible to add it in the future.
|
-3

Mapper Interface to map two objects into one will be like:

@Mapper
public interface CalculateTimeMapper{
     @Mapping(source = "schedule.timezoneId", target = "timezone")
     @Mapping(source = "availabilityRule.startEpoch", target = "startTime")
     AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
                                            availabilityRule, Schedule schedule);
}

You can use below for reference:

https://www.tutorialspoint.com/tutorial_view.htm?cid=mapstruct&pid=mapstruct_mapping_multiple.htm

1 Comment

This is NOT what is being asked: what we are looking for is a way to COMBINE properties from 2 sources to calculate 1 property in the target bean. And what you are saying here is how to get the value of one target from one source property and another target property from a second source property; that's straightforward.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.