You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
nullValueIterableMappingStrategy in 1.5.0.RC1 not working properly #2843
I've played around with mapstruct and I have come across a bug about initializing arrays. The following is taken from your guide
However, MapStruct also offers a more dedicated way to control how collections / maps should be mapped. e.g. return default (empty) collections / maps, but return null for beans.
For collections (iterables) this can be controlled through:
MapperConfig#nullValueIterableMappingStrategy
so I figured that if nullValueIterableMappingStrategy is set to NullValueMappingStrategy.RETURN_NULL then this piece of code
public class TestVo {
private String id;
private List<String> list = new ArrayList<>();
}
public class TestEntity {
private String id;
private List<String> list = new ArrayList<>();
}
The reason why this is not working as expected is because this is using a direct assignment. We are going to have a similar problem for direct assignment of beans as well.
If I change the list to List<Integer> in own of the beans then it works as expected.
Not sure what the best approach for fixing this would be.
We need to investigate what happens when we NullValueMappingStrategy.RETURN_DEFAULT. If that one maps to an empty list then everything works as expected most likely.
I feel like OP's repro is muddied a bit due to the default value shenanigans. The issue is that NullValueMappingStrategy.RETURN_NULL most definitely does not work 'as expected'. Minimum reproduction:
@daniel-frak I am not sure that I understand your comment.
You are saying that
List<String> property1 = null;
List<String> list = testClass1.property1();
if ( list != null ) {
property1 = newArrayList<String>( list );
}
else {
property1 = null; // This is missing in the generated code
}
is incorrect and that the null assignment to property1 is not there when using NullValueMappingStrategy.RETURN_NULL, in your example property1 is already set to null, so if list is null the else branch you are proposing is obsolete. However, in the OP example the target is already set to the empty string in the target field declaration.
publicclassTestVo {
privateStringid;
privateList<String> list = newArrayList<>();
}
In any case, as I said in #2843 (comment), if there is no direct assignment then the mapping strategy works properly. The mapping strategies is applied on the methods and not the direct property mappings. That's why this issue is marked for team discussion
@filiphr I am saying it is a bug that the generated code is missing the line I annotated with // This is missing in the generated code.
If list is null, that does not mean that property1 is also null (though I admit the example above is not the best to showcase that).
Consider that MapStruct might be used for updating an existing entity. The purpose of the update might be to set a non-null property to null. Currently, this non-null property will stay non-null, because a null property set is being ignored.
I'm not sure what you mean by 'direct assignment'. Could you elaborate on that?
@daniel-frak updates are a different thing then what is being asked in the issue. I would suggest that you open a different issue for the update and the missing setting of null.
I'm not sure what you mean by 'direct assignment'. Could you elaborate on that?
Direct assignment happens when the source and target have the same type e.g. List<String>. However, the direct assignment is not affecting the update example you shared, that is entirely different problem
Noticing this issue as well.
If I have a class which has the following declared at field level:
private List<String> myField = new ArrayList<>()
And then I want to create a new instance of this class, using a mapstruct from another class, with this entry for example (or it could be missing and the field names match):
Even if I pass an object with a null value for myFieldInAnotherClass, the value of myField is still just resulting in an empty list, rather than null, due to the value being ignored in the generated code rather than explicitly set.
As mentioned previously, having defaultExpression = java(null) works, as does having a mapper of default <T> List<T> from(List<T> source) { return source; }, but I don't think this should be required when SET_TO_NULL is the default?
Activity
filiphr commentedon May 30, 2022
The reason why this is not working as expected is because this is using a direct assignment. We are going to have a similar problem for direct assignment of beans as well.
If I change the list to
List<Integer>in own of the beans then it works as expected.Not sure what the best approach for fixing this would be.
filiphr commentedon Jun 6, 2022
We need to investigate what happens when we
NullValueMappingStrategy.RETURN_DEFAULT. If that one maps to an empty list then everything works as expected most likely.christiangoudreau commentedon Aug 25, 2022
I've tried this :
And it doesn't work, that produce this:
That is using MapStruct 1.4.2
christiangoudreau commentedon Aug 25, 2022
I just realised that
ClientDocumentDto to(ClientDocument clientDocument);was missing...But still, using the strategy didn't produce en empty ArrayList for labels.
I had to use this strategy to replace the null array by an empty array. :
marialenak commentedon Mar 28, 2023
Is this resolved? Is there a newer version to cover this case?
daniel-frak commentedon Aug 28, 2023
I feel like OP's repro is muddied a bit due to the default value shenanigans. The issue is that
NullValueMappingStrategy.RETURN_NULLmost definitely does not work 'as expected'. Minimum reproduction:Produces:
Instead of the expected:
EDIT:
It seems adding a custom mapping acts as a workaround to this issue:
However, it would be nice not to have to remember to add it to every single iterable property on the class.
filiphr commentedon Sep 10, 2023
@daniel-frak I am not sure that I understand your comment.
You are saying that
is incorrect and that the
nullassignment toproperty1is not there when usingNullValueMappingStrategy.RETURN_NULL, in your exampleproperty1is already set tonull, so iflistisnullthe else branch you are proposing is obsolete. However, in the OP example the target is already set to the empty string in the target field declaration.In any case, as I said in #2843 (comment), if there is no direct assignment then the mapping strategy works properly. The mapping strategies is applied on the methods and not the direct property mappings. That's why this issue is marked for team discussion
Note:
daniel-frak commentedon Sep 11, 2023
@filiphr I am saying it is a bug that the generated code is missing the line I annotated with
// This is missing in the generated code.If
listis null, that does not mean thatproperty1is also null (though I admit the example above is not the best to showcase that).Consider that MapStruct might be used for updating an existing entity. The purpose of the update might be to set a non-null property to null. Currently, this non-null property will stay non-null, because a null property set is being ignored.
I'm not sure what you mean by 'direct assignment'. Could you elaborate on that?
Here's an updated example with a failing test:
filiphr commentedon Sep 16, 2023
@daniel-frak updates are a different thing then what is being asked in the issue. I would suggest that you open a different issue for the update and the missing setting of
null.Direct assignment happens when the source and target have the same type e.g.
List<String>. However, the direct assignment is not affecting the update example you shared, that is entirely different problemjamfor999 commentedon Jul 9, 2024
Noticing this issue as well.
If I have a class which has the following declared at field level:
private List<String> myField = new ArrayList<>()And then I want to create a new instance of this class, using a mapstruct from another class, with this entry for example (or it could be missing and the field names match):
@Mapping(target="myField", source="myFieldInAnotherClass")Even if I pass an object with a null value for
myFieldInAnotherClass, the value ofmyFieldis still just resulting in an empty list, rather than null, due to the value being ignored in the generated code rather than explicitly set.As mentioned previously, having
defaultExpression = java(null)works, as does having a mapper ofdefault <T> List<T> from(List<T> source) { return source; }, but I don't think this should be required whenSET_TO_NULLis the default?