Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping from Enum to String - error (Cannot map to final type) #99

Closed
farrukhmpk opened this issue Apr 9, 2014 · 12 comments
Closed

Mapping from Enum to String - error (Cannot map to final type) #99

farrukhmpk opened this issue Apr 9, 2014 · 12 comments
Labels

Comments

@farrukhmpk
Copy link

Hi,
I am trying to map two objects, where the source has an Enum as property and the respective property in destination is of String type.
The properties are mapped correctly, but I receive (Cannot map to final type) error.
I am using 0.6.3 version of the model mapper. The issue seems to be present in 0.6.4 as well.

As you can see from the code snippet, I have tried using Converter as well, but that didn't help much either.

The relevant code is below.


public class SourceObject implements Serializable {
    private String name;
    private String description;
    private StatusEnum status;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public StatusEnum getStatus() {
        return status;
    }
    public void setStatus(StatusEnum status) {
        this.status = status;
    }
}

public class DestinationObject implements Serializable {
    private String name;
    private String description;
    private String status;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    public String getStatus() {
        return status;
    }
    public void setStatus(String status) {
        this.status = status;
    }
}

public enum StatusEnum {
    INITIATED, SUCCESS, FAILURE;
}

public class MessageMapper {
//This is a utility class
    private static ModelMapper mapper = new ModelMapper();
    static {
        try {
            mapper.getConfiguration().setMatchingStrategy(
                    MatchingStrategies.STRICT);

            final Converter<StatusEnum, String> converter = new AbstractConverter<StatusEnum, String>() {
                @Override
                protected String convert(StatusEnum s) {
                    return s.name();
                }
            };

            mapper.addMappings(new PropertyMap<SourceObject, DestinationObject>() {
                @Override
                protected void configure() {
                    // using(converter).map().setStatus(source.getStatus().name());
                    map().setStatus(source.getStatus().name());
                }
            });

        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

    public static ModelMapper getMapper() {
        return mapper;
    }

    public static <T> T translate(Object source, Class<T> target) {
        return mapper.map(source, target);
    }

}

public class ModelMappingTester {
    public static void main(String[] args) {
        SourceObject src = getSourceObject();
        DestinationObject dest = MessageMapper.translate(src,
                DestinationObject.class);

        System.out.println("name: " + dest.getName());
        System.out.println("description: " + dest.getDescription());
        System.out.println("status: " + dest.getStatus());
    }

    private static SourceObject getSourceObject() {
        SourceObject src = new SourceObject();
        src.setName("John Doe");
        src.setDescription("John Doe is an analyst.");
        src.setStatus(StatusEnum.SUCCESS);

        return src;
    }
}

The error is as follows:


org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) Cannot map to final type.

1 error
    at org.modelmapper.internal.Errors.throwConfigurationExceptionIfErrorsExist(Errors.java:239)
    at org.modelmapper.internal.ExplicitMappingBuilder.build(ExplicitMappingBuilder.java:176)
    at org.modelmapper.internal.TypeMapImpl.addMappings(TypeMapImpl.java:72)
    at org.modelmapper.internal.TypeMapStore.getOrCreate(TypeMapStore.java:101)
    at org.modelmapper.ModelMapper.addMappings(ModelMapper.java:93)

Any help will be greatly appreciated.

@jhalterman
Copy link
Member

This represents one of the limitations of ModelMapper (and any library that proxies types), where we cannot proxy Enums since they're marked as final. The solution is to create your mapping such as this:

map(source.getStatus()).setStatus(null);

@mystarrocks
Copy link

I have a similar problem where the source type in my case in an enum called Status and the target type is an Integer that can be obtained by calling Status.code(). Is there any way to get it work in my case? I don't see either of these addMappings would working:

map(source.getStatus().code()).setStatus(null);
map().setStatus(source.getStatuts().code());

@beiyeren
Copy link

map(source.getStatus()).setStatus(null); not work

how can i fix it?

@rhuankarlus
Copy link

I have the same problem described here.
Why is this issue closed?

MM version: 0.7.5

@alexspring123
Copy link

I have the same problem,who fix it?

@ghost
Copy link

ghost commented Sep 15, 2017

I too have the same problem. Is there any solution to this?

@psamwel
Copy link

psamwel commented Jan 18, 2018

Only way I could get around it in my scenario was to create a Converter<EnumSourceType, EnumDestType> like:

public static Converter<EnumSourceType, EnumDestType> enumConverter = new Converter<EnumSourceType, EnumDestType>()
    {
        @Override
        public EnumDestType convert(MappingContext<EnumSourceType, EnumDestType> mappingContext) {
            if(mappingContext.getSource() != null) {
                return EnumDestType.valueOf(mappingContext.getSource().toString());
            }
            return null;
        }
    };

And then specify the conversion in the PropertyMap<SourceObject, DestObject> as:

using(enumConverter).map().setStatus(this.<EnumDestType>source("getStatus"));

@javeednawab
Copy link

javeednawab commented Mar 30, 2021

I am facing this issue even for all (not only enums all data types ) mismatch types , no matter how I configure mappings I end up getting same exception

Cannot map to final type ***Builder

I see that in the the generated model of my pojo builders are marked as final .

  public static final class Builder extends
      com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
      // @@protoc_insertion_point(builder_implements:com.***)
      com.xxx.xx.xx..XXOrBuilder { 

pom.xml for codegen of proto files

<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.2</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> </plugin> </plugins> </build>

how I do mismatch configuration

TypeMap<Src, DestProto.Builder> builderTypeMap = mapper.createTypeMap(Src.class, DestProto.Builder.class); chassisBuilderTypeMap.addMappings(map -> map.map(Src::getATTr, DestProto.Builder::setMisMatchAttrType));

@rana2884
Copy link

rana2884 commented Apr 8, 2021

PropertyMap<Staff, StaffDto> staffMap =
            new PropertyMap<>() {
                protected void configure() {
                    using(enumConverter).map(source.getStatus()).setStatus(null);
                }
            };
 Converter<Status, String> enumConverter =
            ctx -> ctx.getSource() == null ? null : ctx.getSource().getName();

This worked for me

@daoudasylla
Copy link

@rana2884 thanks, your solution worked for me too.

@AloisSeckar
Copy link

Sorry for doing necromancy here, but I am facing quite the same issue. Thanks to comments here I managed to get my bundle up and running, but I think I need a little explanation:

using(enumConverter).map(source.getStatus()).setStatus(null);

With this line of code I will end up with status being actually mapped to the correct incomming value? If yes, why? Or will it be null and this is just to overcome the mapping error?

@dlehammer
Copy link

Hi @AloisSeckar,

With this line of code I will end up with status being actually mapped to the correct incomming value?

Correct, as the enumConverter will be invoked with the source.getStatus() value as input and the ModelMapper will apply the output to the target.setStatus(<output>).

If yes, why? Or will it be null and this is just to overcome the mapping error?

As setters, like the setStatus(Status status), has a method signature with an argument - the Java compiler requires an argument at compile time.
The null is overridden runtime by the ModelMapper 🤓

(this "final type" issue is similar to #391 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests