ModelMapper won't map composed field

178 Views Asked by At

I have a hard time working around modelmapper. Whenever a non trivial mapping is used an exception will be thrown.

In a nutshell i have:

ModelMapper m = new ModelMapper();
 
      m.createTypeMap(Listing.class, Announce.class)
 .addMapping(listing -> listing == null || listing.getLink() == null || StringUtils.isBlank(listing.getLink().getHref()) ? null : String.format("https://%s%s", domain, listing.getLink().getHref()), Announce::setLink);

And then further on

modelMapper.map(listing, Announce.class);

Which throws an exception:

2023-10-11 17:20:11.465 ERROR 12800 --- [pool-3-thread-1] b.c.f.c.application.Crawler              : unpredicted error: 

org.modelmapper.MappingException: ModelMapper mapping errors:

1) Error mapping br.com.a.b.model.c.Listing to br.com.a.b.model.a.Announce

1 error
    at org.modelmapper.internal.Errors.throwMappingExceptionIfErrorsExist(Errors.java:386) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:80) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:589) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.ModelMapper.map(ModelMapper.java:422) ~[modelmapper-3.1.1.jar:na]
    at br.com.a.b.application.Crawler.lambda$initializeCrawler$2(Crawler.java:143) ~[classes/:na]
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264) ~[na:na]
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
    at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Caused by: org.modelmapper.MappingException: ModelMapper mapping errors:

1) Failed to get value from br.com.a.b.model.c.Listing.getLink()

1 error
    at org.modelmapper.internal.Errors.toMappingException(Errors.java:257) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.PropertyInfoImpl$MethodAccessor.getValue(PropertyInfoImpl.java:104) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.resolveSourceValue(MappingEngineImpl.java:197) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:170) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:151) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:105) ~[modelmapper-3.1.1.jar:na]
    at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:71) ~[modelmapper-3.1.1.jar:na]
    ... 9 common frames omitted
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at org.modelmapper.internal.PropertyInfoImpl$MethodAccessor.getValue(PropertyInfoImpl.java:99) ~[modelmapper-3.1.1.jar:na]
    ... 14 common frames omitted

The point is... if the source class doesn't have a getter that returns EXACTLY what you want to map, modelmapper won't work, so everytime that I have a complex mapping I always create the necessary getters and do whatever transformation I need in them [which is really wrong imho]

BUT IN THIS CASE I'M READING AN ENV VAR.

I don't want to inject an env var into a domain class because, so I need to do it without getters.

What can I do to solve this?

1

There are 1 best solutions below

0
Rafael Lima On

Following the suggestion from @Chaosfire and other stuff i saw online i added a custom converter

.addMappings(mapping -> {
          mapping.using((Converter<Link, String>) context -> context.getSource() == null || StringUtils.isBlank(context.getSource().getHref()) ? null : String.format("https://%s%s", domain, context.getSource().getHref())).map(Listing::getLink, Announce::setLink);
       })

solved the problem