'@AfterMapping mehod is not called in generated class
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?
Solution 1:[1]
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.
Solution 2:[2]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Filip |
| Solution 2 |
