javamapstructaws-sdk-java-2.0

MapStruct with AWS SDk V2 Builder


For internal mapping, I created the POJO / DTO for the AWS SDK Models V2

To map the variables I am using the MapStruct using the Mapper annotation but the Implementation generated during the annotation processing is not using any getters, but the setter with the builder is added properly but with null

@Mapper
public interface Ec2Mapper {
LaunchTemplateBlockDeviceMappingModel mapLaunchTemplateBlockDeviceMappingModel(
      LaunchTemplateBlockDeviceMapping launchTemplateBlockDeviceMapping);
}


@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2021-12-08T15:58:00+0530",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-6.4.1.jar, environment: Java 1.8.0_292 (AdoptOpenJDK)"
)
public class Ec2MapperImpl implements Ec2Mapper {

    @Override
    public LaunchTemplateBlockDeviceMappingModel mapLaunchTemplateBlockDeviceMappingModel(LaunchTemplateBlockDeviceMapping launchTemplateBlockDeviceMapping) {
        if ( launchTemplateBlockDeviceMapping == null ) {
            return null;
        }

        String deviceName = null;
        String virtualName = null;
        LaunchTemplateEbsBlockDeviceModel ebs = null;
        String noDevice = null;

        LaunchTemplateBlockDeviceMappingModel launchTemplateBlockDeviceMappingModel = new LaunchTemplateBlockDeviceMappingModel( deviceName, virtualName, ebs, noDevice );

        return launchTemplateBlockDeviceMappingModel;
    }
}

AWS SDK V2 Models don't have getters with the prefix get, it directly the name of the variable, how to configure the map struct for this scenario.


Solution

  • This naming strategy is working for me for AWS ADK v2 ver 2.20.103:

        public class AwsSdkAccessorNamingStrategy extends DefaultAccessorNamingStrategy {
        
            private final Set<String> NOT_GETTER = Set.of("toString", "hashCode", "sdkFields", "builder", "toBuilder", "serializableBuilderClass");
        
            @Override
            public boolean isGetterMethod(ExecutableElement method) {
                if (isAwsPackage(method)) {
                    String name = method.getSimpleName().toString();
                    return method.getParameters().isEmpty() && !name.startsWith("has") && !NOT_GETTER.contains(name);
                }
                return super.isGetterMethod(method);
            }
        
            @Override
            public String getPropertyName(ExecutableElement method) {
                return isAwsPackage(method) ? method.getSimpleName().toString() : super.getPropertyName(method);
            }
        
            private boolean isAwsPackage(ExecutableElement method) {
                return elementUtils.getPackageOf(method).getQualifiedName().toString().startsWith("software.amazon.awssdk");
            }
        }
    

    How to actually use naming strategy for MapStruct see MapStruct reference and example at mapstruct-examples

    It looks what is the actual class package so it won't break your existing mappers and if class from AWS SDK is found, use any method that does not have parameters, does not start with 'has' and is not one of non-getter methods. Feel free to improve on this.

    Edit: this works for mapping AWS SDK v2 classes as source. For mapping those classes as target, you just add new builder naming strategy class.