-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Expected behavior
If a field name like xNameField is present in both source and target, MapStruct should recognize that field and generate a mapping for it, no matter whether the target class is using builders or not.
Actual behavior
If the target class provides a builder (generated via Lombok's @Builder) with a method TargetClass.TargetClassBuilder.xNameField(), MapStruct ignores that field completely.
Removing the builder from the target class or telling MapStruct not to use it (builder = @Builder(disableBuilder = true)) resolves the problem, although in the latter case, the generated code still is a bit weird:
public TargetClass map(SourceClass src) {
if ( src == null ) {
return null;
}
String someField = null;
String someOtherField = null;
someField = src.getSomeField();
someOtherField = src.getSomeOtherField();
String xNameField = null;
TargetClass targetClass = new TargetClass( someField, xNameField, someOtherField );
targetClass.setXNameField( src.getXNameField() );
return targetClass;
}
What also works is adding an explicit mapping with intentionally wrong capitalization, @Mapping(target = "xNameField", source = "XNameField") generates the correct mapping when it clearly shouldn't -- while @Mapping(target = "xNameField", source = "xNameField") causes an error "No property named "xNameField" exists in source parameter(s). Did you mean "XNameField"?"
Steps to reproduce the problem
Mapping source:
import lombok.Data;
@Data
public class SourceClass {
private String someField;
private String xName;
private String someOtherField;
}
Mapping target:
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class TargetClass {
private String someField;
private String xNameField;
private String someOtherField;
}
...and a simple mapper:
import org.mapstruct.Mapper;
@Mapper
public interface PocMapper {
TargetClass map(final SourceClass src);
}
Result is:
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2026-02-20T10:51:20+0100",
comments = "version: 1.6.3, compiler: javac, environment: Java 21.0.7 (Eclipse Adoptium)"
)
public class PocMapperImpl implements PocMapper {
@Override
public TargetClass map(SourceClass src) {
if ( src == null ) {
return null;
}
TargetClass.TargetClassBuilder targetClass = TargetClass.builder();
targetClass.someField( src.getSomeField() );
targetClass.someOtherField( src.getSomeOtherField() );
return targetClass.build();
}
}
What works
- Removing the
@BuilderfromTargetClass @Mapper(builder = @Builder(disableBuilder = true)), although MapStruct still seems to be a bit confused (see previous section)
What works, even though it clearly should not
- adding
@Mapping(target = "xNameField", source = "XNameField"), with a capital X in the source
What makes it even worse, even though it should work
- adding
@Mapping(target = "xNameField", source = "xNameField"), with the correct lower-case x in the source, lead to an error with the helpful suggestion to use "XNameField" instead
MapStruct Version
MapStruct 1.6.3