3
votes

I found this example about Iterable to Non-Iterable mapping using Qualifier :

https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-iterable-to-non-iterable

But how to make this mapping able to map nested properties (using dot annotation)?

E.g. mapping the field xyz of first element of a collection in the source object to a plain field on the target object?

The example define a Qualifier

@Qualifier  
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.SOURCE)  
public @interface FirstElement {
}

then define a Custom Mapper

public class MapperUtils {
    @FirstElement
    public <T> T first(List<T> in) {
        if (in != null && !in.isEmpty()) {
            return in.get(0);
        }
        else {
            return null;
        }
    }
}

and, finally, the mapping is defined as

@Mapping(target = "emailaddress", source = "emails", qualifiedBy = FirstElement.class )

But if I would like to extract from the first element of emails collection a specific field, e.g. like I would have done with code emails.get(0).getEmailAddress?

For example, I expect to write a mapping like this :

@Mapping(target = "emailaddress", source = "emails[0].emailAddress")
2

2 Answers

2
votes

You just need to change the MapperUtils

public class MapperUtils {
    @FirstElement
    public String firstEmailAddress(List<Person> in) {
        if (in != null && !in.isEmpty()) {
            return in.get(0).getEmailAddress();
        }
        else {
            return null;
       }
    }
}

Basically the parameter of the Annotated method should have the Iterable that you want to map from, and the return type should be the Non-Iterable that you want to map to.

If you do not want to create a custom mapping for the mapping an alternative is to use the expression attribute.

For example:

@Mapping(target = "emailaddress", expression = "emails != null && !emails.isEmpty() ? emails.get(0).getEmailAddress() : null")

However, be careful using the expression can lead to compile time problems if you make a mistake. MapStruct does not do any check for the validity of the expression and uses it as is.

0
votes

Another option is to use the below line in your mapper This uses a helperclass to help with the conversion

@Mapping(target = "emailaddress", qualifiedByName={"helperClass", "emailsToAddress"}, 
source = "emails")

add the helperclass in the components used part of @Mapper

@Mapper(        
        componentModel="spring",
        uses ={                 
                helperClass.class
       },               
       ) 

The helper class would look something like

@Component
@Named("helperClass")
public class helperClass {      

@Named("emailsToAddress")
    public String emailsToAddress(List<Email> emails) {
        if(emails != null || !emails.isEmpty )
            return emails.get(0).getAddress();
        else
            return null;
    }