简体   繁体   中英

How to ignore some fields in MapStruct when using the new @SubclassMapping annotation?

Initial situation

Using current 1.5.0.Beta2 MapStruct release with JDK 13.

Domain Model

class Wrapper {
    private Fruit fruit;
}
abstract class Fruit {
    private int weight;
    /* ... */
}

class Apple extends Fruit {
    /* ... */
}

class Banana extends Fruit {
    /* ... */
}

(Corresponding 1:1 DTOs omitted)

Mappers

Mapper for Wrapper class

@Mapper(uses = {FruitMapper.class})
public interface WrapperMapper {
    WrapperDto map(Wrapper wrapper)
}

Mapper for Fruit class(es)

MapStruct Mapper for abstract Fruit class with method signature and annotations:

@Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
public interface FruitMapper {
    @SubclassMapping(source = Apple.class, target = AppleDto.class)
    @SubclassMapping(source = Banana.class, target = BananaDto.class)
    FruitDto map(Fruit fruit);
}

Problem

The above works fine until a field needs to be ignored on the referenced abstract class (eg the weight of a Fruit ). Putting this annotation to the WrapperMapper map method...

@Mapping(target = "fruit.weight", ignore = true)
WrapperDto map(Wrapper wrapper)

...leads to The return type FruitDto is an abstract class or interface. Provide a non abstract / non interface result type or a factory method. The return type FruitDto is an abstract class or interface. Provide a non abstract / non interface result type or a factory method. compile error.

Question

Is there a way to skip fields in MapStruct mapping as outlined without getting this compile error?

Cause

What happens is that because you designate it on the WrapperMapper it will not directly use the FruitMapper. Instead the WrapperMapper implementation will get a new mapping method generated to map fruits. MapStruct fails at this attempt and gives you that compile error.

Solution

If you add that ignore to the FruitMapper it would inherit it to both subclassmappings:

        @SubclassMapping( source = Apple.class, target = AppleDto.class )
        @SubclassMapping( source = Banana.class, target = BananaDto.class )
        @Mapping( target = "weight", ignore = true )
        FruitDto map( Fruit fruit );

In case you want to have a mapping with weights and one without weights you could add the @org.mapstruct.Named annotation to the one without weights and then use this at the WrappedMapper.

like this:

    @Mapper(subclassExhaustiveStrategy = SubclassExhaustiveStrategy.RUNTIME_EXCEPTION /*...*/)
    public interface FruitMapper {
        @SubclassMapping( source = Apple.class, target = AppleDto.class )
        @SubclassMapping( source = Banana.class, target = BananaDto.class )
        FruitDto map( Fruit fruit );

        @SubclassMapping( source = Apple.class, target = AppleDto.class )
        @SubclassMapping( source = Banana.class, target = BananaDto.class )
        @Mapping( target = "weight", ignore = true )
        @Named( "NoWeights" )
        FruitDto map( Fruit fruit );
    }

    @Mapper(uses = {FruitMapper.class})
    public interface WrapperMapper {
        @Mapping( target = "fruit", qualifiedByName = "NoWeights" )
        WrapperDto map(Wrapper wrapper)
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM