#3081·mapstruct

NullValuePropertyMappingStrategy.IGNORE is ignored on final Lists

Author: domi-87Created Nov 14, 2022Updated Sep 10, 2026
Labelsbug

Expected behavior

I would expect that if "nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE" is set, the target.list.clear() is called only if source.list is not NULL. I would expect the same behavior for conditionalExpression. Again, clear() is called first and only then the conditionalExpression.

public ListHolder mapToListHolder(ListHolder target, ListHolderDTO source) {
        if ( source == null ) {
            return target;
        }

        if ( target.getStrings() != null ) {
            List<String> list = source.getStrings();
            if ( list != null ) {
                target.getStrings().clear();
                target.getStrings().addAll( list );
            }
        }

        return target;
    }

Actual behavior

If the target property is a final list, clear is always called first. Regardless of whether the source property is NULL or not.

public ListHolder mapToListHolder(ListHolder target, ListHolderDTO source) {
        if ( source == null ) {
            return target;
        }

        if ( target.getStrings() != null ) {
            target.getStrings().clear();
            List<String> list = source.getStrings();
            if ( list != null ) {
                target.getStrings().addAll( list );
            }
        }

        return target;
    }

Steps to reproduce the problem

import java.util.ArrayList;
import java.util.List;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy;

import ch.epictec.etac.system.mapper.MapStructMCVE.ListHolder;
import ch.epictec.etac.system.mapper.MapStructMCVE.ListHolderDTO;
import lombok.Data;

@Mapper
public interface MapStructMCVE {

    @Data
    public class ListHolderDTO {
        private List<String> strings;
    }

    @Data
    public class ListHolder {
        private final List<String> strings = new ArrayList<>();
    }

    @Mapping(target = "strings", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
    ListHolder mapToListHolder(@MappingTarget ListHolder target, ListHolderDTO source);
    
    /* 
     * Actual behavior
     */
//    public ListHolder mapToListHolder(ListHolder target, ListHolderDTO source) {
//        if ( source == null ) {
//            return target;
//        }
//
//        if ( target.getStrings() != null ) {
//            target.getStrings().clear();
//            List<String> list = source.getStrings();
//            if ( list != null ) {
//                target.getStrings().addAll( list );
//            }
//        }
//
//        return target;
//    }
    
    /* 
     * Expected behavior
     */
//    public ListHolder mapToListHolder(ListHolder target, ListHolderDTO source) {
//        if ( source == null ) {
//            return target;
//        }
//
//        if ( target.getStrings() != null ) {
//            List<String> list = source.getStrings();
//            if ( list != null ) {
//                target.getStrings().clear();
//                target.getStrings().addAll( list );
//            }
//        }
//
//        return target;
//    }

}

MapStruct Version

MapStruct 1.5.3