I have a class that has a field which in turn is an abstract class. I would like MapStruct to be able to map from the incoming DTO to the internal model including all the subfields of the abstract class, but I only get the empty object in return —rightfully so, since that's the way I've defined the mapping function—.
How can I make it so that MapStruct not only maps the top class, but also the field's classes? Do I have to create mappers for the Cat
and Dog
classes too, and then bring them in to the OwnerMapper
class?
Thank you in advance!
@Mapper(componentModel = MappingConstants.ComponentModel.CDI)
public interface OwnerMapper {
@Mapping(source = "pet", target = "pet", qualifiedByName = "mapPet")
Owner toEntity(OwnerDTO owner);
@Named("mapPet")
default Pet mapPet(final PetDTO pet) {
if (pet instanceof CatDTO) {
return new Cat();
} else {
return new Dog();
}
}
}
public class Owner {
private String name;
private Pet pet;
public Owner() { }
// Omitting getters and setters.
}
public abstract class Pet {
public abstract String makeNoise();
}
public final class Dog extends Pet {
private UUID dogTag;
private String name;
public Dog() { }
@Override
public String makeNoise() {
return "Bark!";
}
// Omitting getters and setters.
}
public final class Cat extends Pet {
private boolean bell;
public Cat() { }
@Override
public String makeNoise() {
return "Meow!";
}
// Omitting getters and setters.
public class OwnerDTO {
private String name;
private PetDTO pet;
public OwnerDTO() { }
// Omitting getters and setters.
public abstract class PetDTO {
}
public final class CatDTO extends PetDTO {
private UUID catTag;
private boolean bell;
public CatDTO() { }
// Omitting getters and setters.
public final class DogDTO extends PetDTO {
private UUID dogTag;
private String name;
public DogDTO() { }
// Omitting getters and setters.
Well, I solved it by defining a few more mapping methods and calling them from the named mapper function:
@Mapper(componentModel = MappingConstants.ComponentModel.CDI)
public interface OwnerMapper {
@Mapping(source = "pet", target = "pet", qualifiedByName = "mapPet")
Owner toEntity(OwnerDTO owner);
Cat catToEntity(CatDTO cat);
Dog dogToEntity(DogDTO dog);
@Named("mapPet")
default Pet mapPet(final PetDTO pet) {
if (pet instanceof CatDTO cat) {
return this.catToEntity(cat);
} else if (pet instanceof DogDTO dog) {
return this.dogToEntity(dog);
} else {
throw new IllegalStateException("Unsupported pet type identified");
}
}
}