Home > Mobile >  Error trying to convert objects in MapStruct
Error trying to convert objects in MapStruct

Time:06-18

I'm trying to convert a ClassDomain class into a ClassEntity but it's not working because it's returning the error below and I don't know how to solve it:

error: The return type List<DomainClass> is an abstract class or interface. Provide a non abstract / non interface result type or a factory method.

Class DataDomainClass:

public class DataDomainClass{
    private List<DomainClass> data;
    }

Class EntityClass:

public class EntityClass {

    private String codDist;

    private String numF;

    private String textJus;

    private Boolean valResol;
    }

Convert mapper:

DataDomainClass map(EntityClass entity);

The DomainClass class contains the same fields as the EntityClass class.

Thanks in advance.

CodePudding user response:

MapStruct can't map non-iterable type to iterable, especially mapping non-iterable to a nested collection.

Here's several solutions:

  1. Change method signatures so input and output parameters will be the same type(iterable or non-iterable) and do the wrapping further in code. This is the most preferable solution

  2. Also you can create default method in your interface to do the wrapping there:

    @Mapper
    public interface Entity2DomainMapper {
    
        DomainClass entity2Domain(EntityClass raw);
    
        default DataDomainClass from(EntityClass raw) {
            DomainClass nested = entity2Domain(raw);
            return DataDomainClass.of(List.of(nested));
        }
    }
    

    So instead of calling auto-generated method you'll call default method

  3. Or if you wanna hide all the wrapping inside MapStruct mapper you can use @AfterMapping, but it still will require you to implement the wrapping by yourself

@Mapper
public abstract class Entity2DomainMapper2 {
    abstract DomainClass entity2Domain(EntityClass raw);

    abstract DataDomainClass entity2DataDomain(EntityClass raw);

    @AfterMapping
    protected void afterMapping(EntityClass raw, @MappingTarget DataDomainClass target) {
        if (target.getNested() == null) {
            target.setNested(new ArrayList<>());
        }
        DomainClass nested = entity2Domain(raw);
        target.getNested().add(nested);
    }
}

CodePudding user response:

There is no clear out-of-box implementation. MapStruct does not support mapping from non-iterable to iterable. Workaround described in mapstruct-examples.
But from my point of view the simples solutions are:
Solution 1: custom method for mapping entity to List

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Mapper
public interface DomainMapper {
    DomainMapper INSTANCE = Mappers.getMapper(DomainMapper.class);

    @Mapping(target = "data", source = "entity")
    DataDomainClass map(EntityClass entity);

    DomainClass toDomain(EntityClass entity);

    default List<DomainClass> toList(EntityClass entity) {
        return entity != null ? Collections.singletonList(toDomain(entity)) :
                                new ArrayList<>();
    }
}

Generated code:

public class DomainMapperImpl implements DomainMapper {

    @Override
    public DataDomainClass map(EntityClass entity) {
        if ( entity == null ) {
            return null;
        }
        DataDomainClass dataDomainClass = new DataDomainClass();
        dataDomainClass.data = toList( entity );
        return dataDomainClass;
    }

    @Override
    public DomainClass toDomain(EntityClass entity) {
        if ( entity == null ) {
            return null;
        }
        DomainClass domainClass = new DomainClass();
        domainClass.codDist = entity.codDist;
        domainClass.numF = entity.numF;
        domainClass.textJus = entity.textJus;
        domainClass.valResol = entity.valResol;
        return domainClass;
    }
}

Solution 2: Expression

import org.mapstruct.*;
import org.mapstruct.factory.Mappers;

import java.util.Collections;

@Mapper(imports = {Collections.class})
public interface DomainMapper {
    DomainMapper INSTANCE = Mappers.getMapper(DomainMapper.class);

    @Mapping(target = "data", expression = "java(Collections.singletonList(toDomain(entity)))")
    DataDomainClass map(EntityClass entity);

    DomainClass toDomain(EntityClass entity);
}

Generated code:

public class DomainMapperImpl implements DomainMapper {

    @Override
    public DataDomainClass map(EntityClass entity) {
        if ( entity == null ) {
            return null;
        }
        DataDomainClass dataDomainClass = new DataDomainClass();
        dataDomainClass.data = Collections.singletonList(toDomain(entity));
        return dataDomainClass;
    }

    @Override
    public DomainClass toDomain(EntityClass entity) {
        if ( entity == null ) {
            return null;
        }
        DomainClass domainClass = new DomainClass();
        domainClass.codDist = entity.codDist;
        domainClass.numF = entity.numF;
        domainClass.textJus = entity.textJus;
        domainClass.valResol = entity.valResol;
        return domainClass;
    }
}
  • Related