Search code examples
javaspring-bootmapstruct

Map single property from class to every object in list of other class


Using MapStruct, I would like to map my RecipientDTO to Recipient. I want the AddressDTO from RecipientDTO to become the Address in every Subscription of Recipient.subscriptions. How do I achieve this with MapStruct? My classes look like this:

public class AddressDTO {
    String street;
    String city;
}
public class SubscriptionDTO {
    String category;
}
public class RecipientDTO {
    String name;
    AddressDTO address;
    List<SubscriptionDTO> subscriptions;
}
public class Address {
    String street;
    String city;
}
public class Subscription {
    Address address;
    String category;
}
public class Recipient {
    String name;
    List<Subscription> subscriptions;
}

I have tried specifying a separate mapper interface for Subscription. So

@Mapper
public interface SubscriptionMapper {
  SubscriptionMapper INSTANCE = Mappers.getMapper(SubscriptionMapper.class);

  @Mapping(target = "address", source = "addressDTO")
  Subscription toSubscription(SubscriptionDTO dto, AddressDTO addressDTO);
}

This will generate a mapper for subscription.

But when I do

@Mapper(uses = SubscriptionMapper.class)
public interface RecipientMapper {
  RecipientMapper INSTANCE = Mappers.getMapper(RecipientMapper.class);


  Recipient toRecipient(RecipientDTO recipientDTO);
}

it will not use the mapping method with extra parameter from SubscriptionMapper.

I would like to do something like this if possible:

@Mapping(target = "subscriptions.address", source = "address")
Recipient toRecipient(RecipientDTO recipientDTO);

Solution

  • You need to use @AfterMapping annotation. This annotation allows you to add custom code after the standard mapping process.

    @Mapper
    public interface RecipientMapper {
        RecipientMapper INSTANCE = Mappers.getMapper(RecipientMapper.class);
    
        Recipient toRecipient(RecipientDTO recipientDTO);
    
        @AfterMapping
        default void updateAddresses(RecipientDTO recipientDTO, @MappingTarget Recipient recipient) {
            if(Objects.nonNull(recipient) && !CollectionUtils.isEmpty(recipient.getSubscriptions())){
                recipient.getSubscriptions().forEach(rcpt -> {
                    var address = recipientDTO.getAddress();
                    rcpt.setAddress(Address.builder().city(address.getCity()).street(address.getStreet()).build());
                });
            }
        }
    }