Reputation: 396
I want to map attributes in target object to default (e.g. String as "") if the corresponding attribute in source is null. How can i achieve that ? I see that the
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT
only works in case of update.
Is it possible to achieve something while creating the target object ?
Upvotes: 5
Views: 17497
Reputation: 9418
To assure default values for objects at initialization we can define them either in the constructor or at field declaration.
Example:
class CarEntity {
String type = "SUV"; // default set at declaration
String color; // default set in constructor
String name; // no default, may be null!
public CarEntity() {
this.color = "black"; // default color set in constructor
}
}
Here we have preset defaults for two fields (type
and color
).
If fields are required (as non-null) they should be declared final
and either initialized directly or passed as constructor argument: e.g. the required field final UUID id
can not have a default, it must be unique like new CarEntity( UUID.randomUUID() )
.
Only optional and nullable field is name
. It has no default and can be freely assigned a value from a mapping.
Let's see how we can use MapStruct to set a default value when mapped.
When default values are set in the class already, the mapping can be simplified.
null
value in respective source properties to overwrite the preset defaults in targetMapStruct may support this:
@Mapper
public interface CarMapper {
@Mapping(target = "type", ignore = true)
@Mapping(target = "name", source = "name", defaultValue = "Sample")
@Mapping(target = "color", condition = "isNotEmpty")
CarEntity dtoToCarEntity(CarDTO car);
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
}
How it works:
type
is completely ignored in mapping. It will not be mapped at all. So the default at target is preserved, not modified by mapping.name
field is set to default "Sample"
if null
in source. The default value can also be an empty String like defaultValue = ""
or a defaultExpression
like for an ID field: defaultExpression = "java( UUID.randomUUID().toString() )
.color
exists in source as null
or empty String it will be ignored by the specified condition = "isNotEmpty"
referencing the @Conditional
predicate-method. See the MapStruct feature issue-205.However in 10.10. Conditional Mapping is explained that we can decide on property-mapping with a condition:
A custom condition method is a method that is annotated with
org.mapstruct.Condition
and returns boolean.e.g. if you only want to map a String property when it is not `null, and it is not empty then you can do something like:
@Condition
default boolean isNotEmpty(String value) {
return value != null && !value.isEmpty();
}
This will have the conditional effect we want, here for Strings:
Closely related to default values in mapping are the ignore-strategies. However they seem not applicable to creational mappings, but only on update (as of current version 1.5.0.Beta2).
null
properties (currently on update only)See MapStruct Reference Guide, 10.6. Controlling mapping result for 'null' arguments.
Any annotation parameter nullValuePropertyMappingStrategy
like NullValuePropertyMappingStrategy.IGNORE
or NullValuePropertyMappingStrategy.SET_TO_DEFAULT
has no effect for creational mappings.
There are also strategies to completely ignore unmapped properties. See the tutorial on Baeldung: Ignoring Unmapped Properties with MapStruct.
However this would turn our defaults into something like final
fields, similar to constants. Defaults should are designed to be overwritten when more specific values are given.
Upvotes: 2
Reputation: 21471
If you want to set default values you need to use Mapping#defaultValue
or Mapping#defaultValueExpression
.
We currently do not support doing something similar to the nullValuePropertyMappingStrategy
.
Another approach could be to use a custom @ObjectFactory
that will set the default values in your object. You can read more about Object Factories in the MapStruct documentation
One could also argue that default values should be set in the object construction.
Upvotes: 1
Reputation: 294
In following example the name
attribute is mapped to default string "Sample"
:
@Mapper
public interface CarMapper {
@Mapping(source = "name", target = "name", defaultValue = "Sample")
Car getModelFromEntity(CarEntity carEntity);
}
You can use the parameter defaultValue
on the @Mapping
annotation to map any null
value attribute from the source to the specified default value on the target.
Upvotes: 3