mapstruct / mapstruct/mapstruct

Camelcase fields where the second letter is capitalized are not picked up

Open
#4,000 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Java
Stars
7.7k
Forks
1.1k
PR merge metrics
No merged PRs in 30d

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 @Builder from TargetClass
  • @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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the SourceClass, TargetClass, and PocMapper reproduction using MapStruct 1.6.3 and Lombok @Builder, then inspect the generated PocMapperImpl. Trace how the builder method named xNameField() is detected and compare generation with the builder enabled and disabled. Done means xNameField is mapped correctly in both cases without relying on an incorrectly capitalized explicit mapping.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
65/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.