Reputation: 2535
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
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
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
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
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