Consider the following domain models in which we have two simple source classes A and B:
public
class A
{
private final String a;
//
Getters and setters
}
public
class B
{
private final Integer b;
//
Getters and setters
}
I like to map these two classes to a target class with two fields:
public
class ABTarget
{
private String a;
private B b;
//
Getters and setters
}
I create the following mapper for doing so:
@Mapper
public
interface MyMapper
{
@Mapping(target = "b", source = "b")
ABTarget map(A a, B b);
}
Which generates the following mapper method:
@Override
public ABTarget map(A a, B b) {
ABTarget aBTarget = new ABTarget();
if ( a != null ) {
aBTarget.setA( a.getA() );
}
if ( b != null ) {
aBTarget.setB( b );
}
return aBTarget;
}
This works fine for most cases. But now let’s assume that `b` can be null in which case I want to set a default value for this field, for example `new B(1)`.
I go with the following mapper using a default expression:
@Mapper(nullValuePropertyMappingStrategy = SET_TO_DEFAULT,
nullValueMappingStrategy
= NRETURN_DEFAULT)
public
interface MyMapperWrongSolutionWithDefaultValue
{
@Mapping(target = "b",
source = "b",
defaultExpression = "java(new
models.B(1))")
ABTarget map(A a, B b);
}
resulting in this mapping method:
@Override
public ABTarget map(A a, B b) {
ABTarget aBTarget = new ABTarget();
if ( a != null ) {
aBTarget.setA( a.getA() );
}
if ( b != null ) {
if ( b != null ) {
aBTarget.setB( b );
}
else {
aBTarget.setB( new models.B(1) );
}
}
return aBTarget;
}
Oops, that is not going to work. I understand that this is an edge case, because in this case the property corresponds to the whole source object. Indeed, if we would apply the same concept on the ‘a’ field, it becomes clear why MapStruct generates that seemingly redundant null check:
@Mapper(nullValuePropertyMappingStrategy
= NullValuePropertyMappingStrategy.SET_TO_DEFAULT,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT)
public
interface MyMapperDefaultValueForProperty {
@Mapping(target = "a", source = "a.a", defaultExpression = "java(\"\")")
@Mapping(target = "b", source = "b")
ABTarget map(A a, B b);
}
@Override
public ABTarget map(A a, B b) {
ABTarget aBTarget = new ABTarget();
if ( a != null ) {
if ( a.getA() != null ) {
aBTarget.setA( a.getA() );
}
else {
aBTarget.setA( "" );
}
}
if ( b != null ) {
aBTarget.setB( b );
}
return aBTarget;
}
I managed to come up with a few workarounds, though. For the first workaround I resort to using an expression, instead of a default expression:
@Mapper
public
interface MyMapperWorkingSolutionWithExpression
{
@Mapping(target = "b", expression = "java(map(b))")
ABTarget map(A a, B b);
default B map(B b) {
return b != null? b : new B(1);
}
}
The second workaround boils down to using the after mapping construct:
@Mapper
public
interface MyMapperSolutionWithAfterMapping
{
@AfterMapping
default ABTarget afterMap(@MappingTarget ABTarget t) {
if (t.getB() == null) {
t.setB(new B(1));
}
return t;
}
@Mapping(target = "b", source = "b")
ABTarget map(A a, B b);
}
Yet, wouldn’t it be cool if the default expression would work out-of-the-box for nullable source objects? Is it worthwhile creating a bug report / feature request for this?
--
You received this message because you are subscribed to a topic in the Google Groups "mapstruct-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mapstruct-users/zBAxuWrxHK0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mapstruct-use...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/mapstruct-users/2f1c3a57-324c-4ae3-8396-941c649d1684n%40googlegroups.com.