16

Consider the following POJOs:

public class SchedulePayload {
    public String name;
    public String scheduler;
    public PeriodPayload notificationPeriod;
    public PeriodPayload schedulePeriod;
}

private class Lecture {
    public ZonedDateTime start;
    public ZonedDateTime end;
}

public class XmlSchedule {
    public String scheduleName;
    public String schedulerName;
    public DateTime notificationFrom;
    public DateTime notificationTo;
    public DateTime scheduleFrom;
    public DateTime scheduleTo;
}

public class PeriodPayload {
    public DateTime start;
    public DateTime finish;
}

Using MapStruct, I created a mapper that maps XmlSchedule to a SchedulePayload. Due to "business" "logic", I need to constrain notificationPeriod and schedulePeriod to a Lecture's start and end field values. Here is what I've come up to, using another class:

@Mapper(imports = { NotificationPeriodHelper.class })
public interface ISchedulePayloadMapper
{
    @Mappings({
        @Mapping(target = "name", source = "scheduleName"),
        @Mapping(target = "scheduler", source = "schedulerName"),
        @Mapping(target = "notificationPeriod", expression = "java(NotificationPeriodHelper.getConstrainedPeriod(xmlSchedule, notificationFrom, notificationTo))"),
        @Mapping(target = "schedulePeriod", expression = "java(NotificationPeriodHelper.getConstrainedPeriod(xmlSchedule, scheduleFrom, scheduleTo))")
    })
    SchedulePayload map(XmlSchedule xmlSchedule, Lecture lecture);

}

Is there any way this can be achieved in another way (i.e. another mapper, decorators, etc.)? How can I pass multiple values (xmlSchedule, lecture) to a mapper?

1 Answer 1

25

What you can do is create an @AfterMapping method to populate those parts manually:

@Mapper
public abstract class SchedulePayloadMapper
{
    @Mappings({
        @Mapping(target = "name", source = "scheduleName"),
        @Mapping(target = "scheduler", source = "schedulerName"),
        @Mapping(target = "notificationPeriod", expression = "java(NotificationPeriodHelper.getConstrainedPeriod(xmlSchedule, notificationFrom, notificationTo))"),
        @Mapping(target = "schedulePeriod", expression = "java(NotificationPeriodHelper.getConstrainedPeriod(xmlSchedule, scheduleFrom, scheduleTo))")
    })
    public abstract SchedulePayload map(XmlSchedule xmlSchedule, Lecture lecture);

    @AfterMapping
    protected void addPeriods(@MappingTarget SchedulePayload result, XmlSchedule xmlSchedule, Lecture lecture) {
        result.setNotificationPeriod(..);
        result.setSchedulePeriod(..);
    }
}

Alternatively, you can place the @AfterMapping method in another class that is referenced in @Mapper(uses = ..) or you can use a Decorator (using the mechanisms MapStruct provides, or of your dependency injection framework if you use one).

Sign up to request clarification or add additional context in comments.

6 Comments

@AfterMapping seems a clean approach! Thanks!
Just out of curiosity, is there a way to handle immutable objects?
@RobertGabriel since the latest (1.3.0.Beta1) MapStruct can use Builders to map into Immutable objects. In the answer of this question, this would mean that you can have the builder for SchedulePayload in the @AfterMapping
Yes, you are right. A colleague told me the same thing yesterday. :)
Note when using the builder pattern in your objects, the @MappingTarget should be of type YourBuilder instead of your response type!
|

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.