Florian
Florian

Reputation: 5051

How do I map a class to an interface with Orika?

I'd like to map Java objects to each other, using Orika in the current version 1.5.4.

On the one side, I have immutable objects whose private final properties must be set from a constructor:

public class Source {

    private final String content;

    public Source(String content) {
        this.content = content;
    }

}

On the other side, I have only interfaces:

public interface Destination {

    String getContent();
    setContent(String content);

    static Books create() {
        return TypeFactory.create(Destination.class);
    }

}

I use a generic TypeFactory to produce implementations (proxies) for these instances on the fly:

public class TypeFactory{
    public static <T> T create(Class<T> type) {
        // on the fly constructs a class
        // that implements the Destination interface
        return proxyFactory.createProxyForType(type);
    }
}

The straight-forward way to map these two fails with No concrete class mapping defined for source class:

public Destination map(Source source) {
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Source.class, Destination.class)
        .constructorA("content").byDefault().register();
    MapperFacade mapper = mapperFactory.getMapperFacade();
    return mapper.map(domainBook, Books.class);
}

I assume this is because one side is interfaces only and I need to provide a factory how to create real classes for them, but this fails just the same:

private static class DestinationFactory implements ObjectFactory<Destination> {
    @Override
    public Destination create(Object o, MappingContext mappingContext) {
        return Destination.create();
    }
}

public Destination map(Source source) {
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Source.class, Destination.class)
        .constructorA("content").byDefault().register();
    mapperFactory.registerObjectFactory(new DestinationFactory(), Destination.class);
    MapperFacade mapper = mapperFactory.getMapperFacade();
    return mapper.map(domainBook, Books.class);
}

Can somebody assist in getting this to work?

Upvotes: 1

Views: 960

Answers (1)

Can you provide more details about your error? Because the Object Factory seems to be the best option for you.

Another thing that might make it work for you instead of using the Object Factory is creating a mapper from your Interface to the materialized class that will be generated at Runtime, because Orika will need the reference to a class over runtime. Something like:

public Destination map(Source source) {
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Source.class, Destination.class)
        .constructorA("content").byDefault().register();
    

    mapperFactory.classMap(Destination.class, Destination.create().class).byDefault().register();
    
    MapperFacade mapper = mapperFactory.getMapperFacade();
    return mapper.map(domainBook, Books.class);
}

Creating this mapper from Interface to the Concrete implementation was the trick that made my implementation being able to map to the desired class.

EDIT: Another thing that worked for me, which is related to your issue, and It was not described on the Orika's documentation is using the DefaultMapperFactory.registerConcreteType. This indicates to Orika what are the concrete class for abstract types. They are using this method to indicate the default implementation for the java Collections types. So your code would be like the below, and you still would need to refer to the runtime materialized class that TypeFactory will create.

public Destination map(Source source) {
    MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
    mapperFactory.classMap(Source.class, Destination.class)
        .constructorA("content").byDefault().register();

    mapperFactory.registerConcreteType(Destination.class, Destination.create().class);

    MapperFacade mapper = mapperFactory.getMapperFacade();
    return mapper.map(domainBook, Books.class);
}

Upvotes: 0

Related Questions