Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MapStruct fails to map a nested object which requires external variable

Im trying to use MapStruct to map an object that has a nested object and requires an external variable.

The source -> target mapping is lossy, and requires the external string

The target -> source mapping works and output is generated

I'm using Lombok and my objects are immutable.


//Entities
public class Repro {

    @Value
    @Builder
    public static class Nested {
        @NonNull
        private String id;
        @Nullable
        private String version;
        @NonNull
        private String externalId;
    }

    @Value
    @Builder
    public static class SourceEntity {
        @NonNull
        private String id;
        @NonNull
        private String anotherId;
    }

    @Value
    @Builder
    public static class TargetEntity {
        @NonNull
        private Nested nested;
        @NonNull
        private String anotherId;
    }
}
//Mapper

@Mapper
public interface ReproMapper {

    @Mapping(target = "nested.version", ignore = true)
    @Mapping(source = "source.id", target = "nested.id")
    @Mapping(source = "source.anotherId", target = "anotherId")
    @Mapping(source = "externalId", target = "nested.externalId")
    Repro.TargetEntity fromSource(Repro.SourceEntity source, String externalId);

    @Mapping(source = "nested.id", target = "id")
    @Mapping(source = "anotherId", target = "anotherId")
    Repro.SourceEntity fromTarget(Repro.TargetEntity target);
}

I get the error message (package names omitted):

Can't map property "Repro.SourceEntity source" to "Repro.Nested nested". Consider to declare/implement a mapping method: "Repro.Nested map(Repro.SourceEntity value)

Which tells me to implement a mapping method which is not feasible (since it would construct a partial Nested object) which would fail during the build() call.

Is there a way around this using MapStruct or do I just implement my own mapper ?

like image 469
Hav3n Avatar asked Oct 18 '25 18:10

Hav3n


1 Answers

You could try it like this (handwritten method icm @MappingContext to hand down the externalId:

@Mapper
public interface ReproMapper {

    @Mapping(target = "nested",source = "source")
    @Mapping(target = "anotherId",source = "source.anotherId")
    Repro.TargetEntity fromSource(Repro.SourceEntity source, @Context String externalId);

    //A default impl that delegates to MapStruct generated method
    default Repro.TargetEntity.Nested resolveNested(Repro.SourceEntity source, @Context String externalId) {
        return delegate(source, externalId);
    }

    //Actual Mapping method, generated by MapStruct
    //Note here externalId is not @Context annotated
    @Mapping(target = "version", ignore = true)
    @Mapping(target = "id", source = "source.id")
    @Mapping(target = "externalId", source = "externalId")
    Repro.TargetEntity.Nested delegate(Repro.SourceEntity source, String externalId)

    @Mapping(source = "nested.id", target = "id")
    @Mapping(source = "anotherId", target = "anotherId")
    Repro.SourceEntity fromTarget(Repro.TargetEntity target);
}
like image 59
Sjaak Avatar answered Oct 20 '25 08:10

Sjaak