Reputation: 3270
I'm trying to customize a mapping use a string to determine an object attribute so I wrote this:
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public abstract ProductInput asProductInputFromIdentifier(String identifier);
@AfterMapping
protected void determineIdentifier(String identifier, @MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
}
}
The generated class doesn't call the determineIdentifier method. I found another solution by using directly the Java expression on the asProductInputFromIdentifier method but I really want to write a clear code using the @AfterMapping.
@Mapping(target = "externalId", expression = "java( org.apache.commons.lang3.StringUtils.contains(identifier, '|') ? identifier : null )")
@Mapping(target = "internalId", expression = "java( !org.apache.commons.lang3.StringUtils.contains(identifier, '|') ? identifier : null )")
public abstract ProductInput asProductDetailInputFromIdentifier(String identifier);
I didn't understand why it doesn't work Is it because I don't have an Object in method parameter?
Upvotes: 6
Views: 10045
Reputation: 3724
In case of having lombok @Builder, the afterMapping will never be called.
The issue is always not resolved.
Check the issue on the official repo here
Upvotes: 9
Reputation: 11
As @Filip mentioned, your @Aftermapping method should return the same return type as that of the mapping method.
@AfterMapping
protected ProductInput determineIdentifier(String identifier, @MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
return out;
}
I was using Lombok @Builder annotation on my returned object. When I removed that annotation, my code started working.
Upvotes: -1
Reputation: 3270
To solve the problem, I added the to ProductInput to the abstract method parameters as follow (which is not the requirement). As explains @Rakesh in his answer above Method with @AfterMapping will be executed at the end of map method and it should have same input parameter as map method, but it can interest others in their case:
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public abstract ProductInput asProductInputFromIdentifier(String identifier, ProductInput out);
@AfterMapping
protected void determineIdentifier(String identifier, @MappingTarget ProductInput out) {
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
}
}
But In my case I used the builder = @Builder(disableBuilder = true) to turn off "builders" in MapStruct be it can help someone ;) Mapstruct Builder Now the generated MapperImpl calls the annotated @AfterMapping method.
Upvotes: 3
Reputation: 21393
MapStruct doesn't know how to map from String
to ProductInput
in your case I would suggest that you implement the mapping method on your own.
e.g.
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class ProductMapper {
public ProductInput asProductInputFromIdentifier(String identifier) {
ProductInput out = new ProductInput();
if (StringUtils.contains(identifier, '?')) {
out.setExternalId(identifier);
} else {
out.setInernalId(identifier);
}
return out;
}
}
There is no point in using lifecycle callbacks nor special @Mapping
annotations to perform a simple mapping like in this example.
Upvotes: 2
Reputation: 342
Method with @AfterMapping
will be executed at the end of map method and it should have same input parameter as map method for example in below sample
@Mapper(
componentModel = "spring",
injectionStrategy = InjectionStrategy.CONSTRUCTOR,
uses = {IdentifierMapper.class})
public interface HistoryMapper {
HistoryDynamo toHistoryDynamo(History history);
@AfterMapping
default void changeReason(History history) {
System.out.println(history.getReason());
}
}
Refer github working sample for the same https://github.com/rakesh-singh-samples/map-struct-samples/tree/stack-question-60523230/src/sample/mapstruct/mapper
Upvotes: 6