mapstruct wrapper type and generics

Viewed 751

I am trying to map JsonNullable<List<ChildRequestTO> to Nullable<List<ChildRequestDO>> (see full code below) with mapstruct 1.4.2.Final and I am facing the following error: error: Nullable<List<ChildRequestDO>> does not have an accessible constructor. If I add a constructor for Nullable like

    public Nullable(T value) {
        this.value = value;
        this.isPresent = true;
    }

then I get the following error error: Unmapped target property: "value". Mapping from property "JsonNullable<List<ChildRequestTO>> products" to "Nullable<List<ChildRequestDO>> products".

How do I map complex wrapped types in a generic way?

The following mapping code (part of ChildRequestMapper class and applied in ObjectRequestMapper) solves the problem but I want to solve it in a more generic way:

    @Named("mappingHelper")
    default Nullable<List<ChildRequestDO>> customMapToDOs(JsonNullable<List<ChildRequestTO>> input) {
        if (JsonNullable.undefined().equals(input)) {
            return Nullable.undefined();
        }
        if (input.get() == null) {
            return Nullable.of(null);
        }
        var output= input.get()
                .stream()
                .map(this::mapToDO)
                .collect(Collectors.toList());
        return Nullable.of(output);
    }

Changing the NullableMapper to the code below does not work/compile because I do not know how to tell mapstruct to look for the appropriate mapper to map from T to X.

    public static <T, X> Nullable<X> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
        if (jsonNullable.isPresent()) {
            return Nullable.of(jsonNullable.get());
        }
        return Nullable.undefined();
    }

Full code:

@Mapper(
        unmappedTargetPolicy = ReportingPolicy.ERROR,
        uses = {ChildRequestMapper.class, NullableMapper.class}
)
public interface ObjectRequestMapper {

    @Mapping(target = "slots", source = "slots", qualifiedByName = "mapToSlotDOs")
    ModifyObjectRequestDO mapToDO(ModifyObjectRequestTO input);
}
@Mapper(unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface ChildRequestMapper {

    ChildRequestDO mapToDO(ChildRequestTO input);
}
public class NullableMapper {

    public static <T> Nullable<T> jsonNullableToNullable(JsonNullable<T> jsonNullable) {
        if (jsonNullable.isPresent()) {
            return Nullable.of(jsonNullable.get());
        }
        return Nullable.undefined();
    }
}
public class ModifyObjectRequestTO {

    private JsonNullable<String> name = JsonNullable.undefined();
    private JsonNullable<List<ChildRequestTO>> children = JsonNullable.undefined();

}
public class ModifyObjectRequestDO {

    private Nullable<String> name = Nullable.undefined();
    private Nullable<List<ChildRequestDO>> children = Nullable.undefined();

}
public class Nullable<T> {

    private static final Nullable<?> UNDEFINED = new Nullable<>(null, false);

    private final T value;

    private final boolean isPresent;

    private Nullable(T value, boolean isPresent) {
        this.value = value;
        this.isPresent = isPresent;
    }

    public static <T> Nullable<T> undefined() {
        @SuppressWarnings("unchecked")
        Nullable<T> t = (Nullable<T>) UNDEFINED;
        return t;
    }

    public static <T> Nullable<T> of(T value) {
        return new Nullable<T>(value, true);
    }

    public T get() {
        if (!isPresent) {
            throw new NoSuchElementException("Value is undefined");
        }
        return value;
    }

    public boolean isPresent() {
        return isPresent;
    }

}
0 Answers
Related