Map struct : When source is null, target should NOT be set to null
Asked Answered
S

6

23

I am trying to map nested properties using mapstruct 1.2.0.CR2. (Example map customer.address.houseNumber to userDTO.homeDTO.addressDTO.houseNo ).

Expectation : I do not want to set the addressDTO to null when customer.address is null. Since addressDTO contains "countyname" and other properties which are already set from other different sources.

Please advice if there is property/setting that I could set so that the target it not set to null when source is null.

@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS )
public interface CustomerUserMapperNullCheck {

    @Mapping(source="address", target="homeDTO.addressDTO" )
    void mapCustomer(Customer customer, @MappingTarget  UserDTO userDTO)  ;

    @Mapping(source="houseNumber", target="houseNo" )
    void mapCustomerHouse(Address address, @MappingTarget  AddressDTO addrDTO)  ;

}

I initially tried in single mapping like below

@Mapping(target="homeDTO.addressDTO.houseNo", source="address.houseNumber")
 abstract void mapCustomerHouse(Customer customer, @MappingTarget  UserDTO userDTO)  ; 

Then tried splitting up the mapping, based on https://github.com/mapstruct/mapstruct/issues/649.

Both approaches does not produce the expected result/ Generated method code

 protected void customerToHomeDTO(Customer customer, HomeDTO mappingTarget) {
        if ( customer == null ) {
            return;
        }

        if ( customer.getAddress() != null ) {
            if ( mappingTarget.getAddressDTO() == null ) {
                mappingTarget.setAddressDTO( new AddressDTO() );
            }
            mapCustomerHouse( customer.getAddress(), mappingTarget.getAddressDTO() );
        }
        **else {
            mappingTarget.setAddressDTO( null );   // I dont want to else where addressDTO is set to null.
        }**
    }

The complete generated code is here
https://github.com/mapstruct/mapstruct/issues/1306

Thanks

Spindrift answered 10/10, 2017 at 14:43 Comment(2)
Did the answer in the linked issue solved your problem?Pena
For me it works as explained in the link provided in the issue/question.Janessa
D
42
@Mapper( nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE )
Deepdyed answered 5/9, 2020 at 8:55 Comment(0)
P
4

For me it worked like this:

@Mapper(nullValueMappingStrategy =  NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {
...
}
Perennate answered 6/10, 2021 at 9:57 Comment(0)
M
4

For me only this one works, as explained here: https://github.com/mapstruct/mapstruct/issues/649

@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS )
Manon answered 10/11, 2022 at 10:2 Comment(0)
F
4

This worked for me:

@BeanMapping(nullValuePropertyMappingStrategy =  NullValuePropertyMappingStrategy.IGNORE)
void updateProduct(@MappingTarget Product entity, ProductRequestDto dto);

@Mapping annotation requires "target" option in current (1.5.5.Final) MapStruct release.

Thanks for @tak3shi link on github discussion!

Figurine answered 24/6, 2023 at 13:23 Comment(1)
Adding MappingTarget adnotation as well as BeanMapping(nullValuePropertyMappingStrategy did the trick for me. It seems that without MappingTarget mapper implementation generates but ignoring nullValuePropertyMappingStrategyDemodena
I
2

I'm using Mapstruct 1.5.5.Final

You can use NullValuePropertyMappingStrategy.IGNORE at mapper level to affect all your mapping methods:

    @Mapper(
        nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE
    )
    public interface MyMapper {..}

If you want just to override the mapping for a single value, you can use NullValuePropertyMappingStrategy.IGNORE at mapping level and define a custom mapping method:

    @Mappings({
        @Mapping(target = "json", source = "json", 
                 qualifiedByName = "jsonCustomMapper", 
                 nullValueCheckStrategy = NullValueCheckStrategy.ON_IMPLICIT_CONVERSION)
    })
    Target target(Source source);
    
    ...

    @Named("jsonCustomMapper")
    default String jsonCustomMapper(String json) {
        // Define empty json for null or empty json string
        return (json == null || json.trim().isEmpty()) ? "{}" :json;
    }

Note that ON_IMPLICIT_CONVERSION does not affect custom mapping methods. https://mapstruct.org/documentation/stable/api/org/mapstruct/NullValueCheckStrategy.html#ON_IMPLICIT_CONVERSION

Hope this is useful for you.

Indurate answered 14/11, 2023 at 5:7 Comment(0)
P
0

At field level mapping, I'm using default values to avoid null on sources.

...
@Mapping(source = "sourceString", target = "targetString", defaultValue = "")
...

I admit this is just an answer to the title of this article rather than to the specific question when using complex datatypes. However I came across this when mapping a String to an enum, where a #fromString method handles null input returning the UNDEFINED enum instead of null. Since NullValueCheckStrategy.IGNORE isn't available anymore, defaultValue helped me to avoid null values.

public static AnEnum fromString(String aString) {
    try {
        return aString == null ? UNDEFINED : AnEnum .valueOf(aString.toUpperCase());
    } catch (IllegalArgumentException e) {
        return UNDEFINED;
    }
}

The target class of the Mapping provides a setter for the AnEnum typed variable setting it by calling #fromString:

public void setAnEnum (String aString) {
    this.anEnum = AnEnum.fromString(aString);
}
Preset answered 22/5 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.