Vijayant Bhatia
Vijayant Bhatia

Reputation: 2535

mapping of non-iterable to iterable in mapstruct

I am trying to map a non-iterable value i.e. String to a list of string using mapstruct. So I am using

@Mapping(target = "abc", expression = "java(java.util.Arrays.asList(x.getY().getXyz()))")

Here abc is List<String>

xyz is a String

But for this i need to check for null explicitly.

Is there any better way of maaping a non-iterable to iterable by converting non iterable to iterable.

Upvotes: 2

Views: 15728

Answers (4)

M. Justin
M. Justin

Reputation: 21295

Here is a straightforward solution using a custom method to map a string to a List<String>:

@Mapper
public interface NonIterableToIterableMapper {
    @Mapping(target = "abc", source = "y.xyz")
    Target map(Source source);

    default List<String> toSingletonList(String s) {
        return Collections.singletonList(s);
    }
}

Upvotes: 0

yasi
yasi

Reputation: 473

Add an empty default method in the mapper, e.g. AnimalMapper.toLions(), and overwrite the default method in a mapper decorator, e.g. AnimalMapperDecorator. It works in my test.

@Repository
@Mapper(componentModel = "spring")
@DecoratedWith(AnimalMapperDecorator.class)
public interface AnimalMapper {

    default List<Lion> toLions(Jungle jungle) {
        return null;
    }
}

public abstract class AnimalMapperDecorator implements AnimalMapper {
    @Autowired
    @Qualifier("delegate")
    private AnimalMapper delegate;

    @Override
    public List<Lion> toLions(Jungle jungle) {
        List<Lion> lions = new ArrayList<>();
        Lion king = getKing(jungle);
        Lion queen = getQueen(jungle);
        Lion leo = getLeo(jungle);
        lions.add(king); lions.add(queen); lions.add(leo);

        return lions;
    }
}

class Test {
    @Autowired
    private AnimalMapper animalMapper;

    public void test() {
        Jungle jungle = generateJungle();
        List<Lion> lions = animalMapper.toLions(jungle);
        // Assert lions
    }
}

Upvotes: 1

Pablo Garc&#237;a
Pablo Garc&#237;a

Reputation: 66

Here is an example for non iterable-to-iterable:

public class Source {

    private String myString;

    public String getMyString() {
        return myString;
    }

    public void setMyString(String myString) {
        this.myString = myString;
    }

}

public class Target {

    private List<String> myStrings;

    public List<String> getMyStrings() {
        return myStrings;
    }

    public void setMyStrings(List<String> myStrings) {
        this.myStrings = myStrings;
    }   

}

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

public class NonIterableToIterableUtils {

     @FirstElement
        public List<String> first(String in ) {
            if (StringUtils.isNotEmpty(in)) {
                return Arrays.asList(in);

            } else {
                return null;
            }
        }

}

@Mapper( uses = NonIterableToIterableUtils.class )
public interface SourceTargetMapper {

    SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );

    @Mappings( {
        @Mapping( source = "myString", target = "myStrings", qualifiedBy = FirstElement.class )
    } )
    Target toTarget( Source s );
}

public class Main {

    public static void main(String[] args) {
        Source s = new Source();
        s.setMyString("Item");

        Target t = SourceTargetMapper.MAPPER.toTarget( s );
        System.out.println( t.getMyStrings().get(0));

    }

}

Upvotes: 3

Filip
Filip

Reputation: 21423

There is a iterable-to-non-iterable example in the MapStruct examples repository. Addtionally there is a pending pull request for non-iterable-to-iterable.

In a nutshell you can use a custom method that would do the mapping. You can also use @Qualifier to have more granural control

Upvotes: 3

Related Questions