Edu Castrillon
Edu Castrillon

Reputation: 577

mapstruct ambiguous exception when using helper class

Using mapstruct v1.0.0.Final, I'm facing an ambiguous mapping exception trying to map from SourceType to TargetType:

class TargetType {
  List<TargetTypeChild> children;
  boolean allResults;
}

class SourceType {
  List<SourceTypeChild> children;
  boolean allResults;
}

The mapping that I'm using is:

@Mapper(uses = B.class)
interface A {
    @Mapping(target = "children", source = "children", qualifiedBy = ToTargetType.class)
    TargetType toTargetType (SourceType source);

    @Mapping(target = "children", source = "children", qualifiedBy = ToTargetTypeNoDetails.class)
    TargetType toTargetTypeNoDetails (SourceType source);
}

interface B {
    @Qualifier
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface ToTargetType {}

    @Qualifier
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface ToTargetTypeNoDetails {}

    @ToTargetType
    @IterableMapping(qualifiedBy = ToTargetType.class)
    List<TargetTypeChild> withDetails(List<SourceTypeChild> value);

    @ToTargetTypeNoDetails
    @IterableMapping(qualifiedBy = ToTargetTypeNoDetails.class)
    List<TargetTypeChild> noDetails(List<SourceTypeChild> value);

    @ToTargetType
    @Mappings({ 
      @Mapping(target = "details", source = "details"), 
      ...rest of mapping 
    })
    TargetTypeChild toTargetTypeChild(SourceTypeChild source);

    @ToTargetTypeNoDetails
    @Mappings({ 
      @Mapping(target = "details", ignore = true), 
      ...rest of mapping 
    })
    TargetTypeChild toTargetTypeChildNoDetails(SourceTypeChild source);
}

This does not compile, giving the following exception in both interface A's methods:

Ambiguous mapping methods found for mapping property "List children" to List: List noDetails(List arg0), List withDetails(List arg0).

There is one workaround to this: put both interface A's methods in interface B. That compiles and works. But I need to separate them for business reasons.

Could anyone explain why the first approach doesn't work and the workaround does?

As a bonus question, if I only code 1 method for mapping (no qualifiers), I don't need to even declare the @IterableMapping method, mapstruct knows how to find the "children" methods. Why?

Thank you all!

Upvotes: 0

Views: 2222

Answers (1)

Gunnar
Gunnar

Reputation: 18970

Anyone could explain why the first approach doesn't work and the workaround does?

Your qualifier annotations must at least have retention policy CLASS, only then they will be discovered. That's not needed if everything is defined within the same source file, in which case SOURCE is enough.

As a bonus question, if I only code 1 method for mapping (no qualifiers)

MapStruct will generate (private) iterable mapping methods as needed. Actually it should work also in your original case, seems like that's a glitch we need to fix. I've filed issue #707 for this.

Thanks for reporting this!

Upvotes: 2

Related Questions