#4128·mapstruct

NullValuePropertyMappingStrategy.IGNORE strategy ignored for Optional fields

Author: ijioioCreated Sep 16, 2026Updated Sep 16, 2026
Labelsbug

Expected behavior

The NullValuePropertyMappingStrategy.IGNORE strategy should be applied to Optional fields in exactly the same way as it is applied to other field types. Its behavior is clearly defined in the Javadoc:

If a source bean property equals null the target bean property will be ignored and retain its existing value.

Instead, NullValuePropertyMappingStrategy.IGNORE is effectively ignored for Optional fields. This also doesn't align with common JSON mapping semantics, where an absent field is ignored (i.e. nothing is done), while an explicitly provided null value is mapped to Optional.empty().

This approach is often used to cover patch semantics during mapping. A patch needs to distinguish between three states, which is why Optional is used:

  • null - ignore the field and keep its existing value
  • Optional.empty() - clear the field by setting it to null
  • Optional.of(value) - update the field with the wrapped value

Previously, NullValuePropertyMappingStrategy.IGNORE did exactly what was expected: it applied the necessary null checks and preserved the distinction between an absent field and an explicitly provided empty value.

Quite a few production systems are already built around these semantics. The recent changes therefore introduce breaking changes for existing applications that rely on this distinction.

Actual behavior

The NullValuePropertyMappingStrategy.IGNORE strategy is simply ignored, and no null check is generated:

Steps to reproduce the problem

  1. Create source and target objects with Optional field:
java
public class Source {
    private Optional<String> foo;
    public Optional<String> getFoo() { return foo; }
    public void setFoo(Optional<String> foo) { this.foo = foo; }
}

public class Target {
    private Optional<String> foo;
    public Optional<String> getFoo() { return foo; }
    public void setFoo(Optional<String> foo) { this.foo = foo; }
}
  1. Create mapper to map source to target:
java
@Mapper(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface InvalidMapper {
    public static final InvalidMapper INSTANCE = Mappers.getMapper(InvalidMapper.class);
    public Target map(Source source);
}
  1. Check generated mapper implementation:
java
@Override
public Target map(Source source) {
    if ( source == null ) {
        return null;
    }

    Target target = new Target();

    target.setFoo( source.getFoo() );

    return target;
}

No null checks are applied

MapStruct Version

1.7.0.Beta2