How to map cache to a list of DTO object? - java

I have this cache that is basically a list of a DTO:
ClassA:
#Cacheable("MyList")
public List<MyObjectDTO> setCachedList(){
return api.getList(); //call to another api to fetch that list
}
ClassB:
public List<MyObjectDTO> getCachedList(){
if(!CacheManager.getCacheNames().contains("MyList"){
ClassB.setCachedList();
}
Cache cache = CacheManager.getCache("MyList");
Type listType = new TypeToken<List<MyObjectDTO>>(){}.getType(); //error occurs here
List<MyObjectDTO> returnList = modelMapper.map(cache, listType);
return returnList;
I get the following error in above code:
Failed to instantiate instance of destination java.util.List. Ensure that java.util.List has a non-private no-argument constructor.
I saw a similar question but since I am using cache of a list, I cannot extend it to concrete class.

I don't think the error is in the line you mention but in the one right after. Type returns List, but List is an interface and therefore impossible to instantiate, your mapper must be catching this exception and throwing the error.
You should try another way for getting the cache type, maybe cache.getClass() or something of the sort will do the trick.

I was able to resolve the issue by passing cache.get(SimpleKey.EMPTY).get() in the modelMapper instead of cache.

Related

MapStruct - Map list of objects in single object

I am getting a list of objects from 3rd party but it will always contain one object only. So at my end in target I have created it as an object rather than list. That object contains multiple lists inside it just like source object.
This is how I am trying to map a list to an object. ChargeTransaction contain orderInvoice as an object and not a list. For list which are inside ChargeTransaction I have created separate mappers. I dont want to write java code in #afterMapping because then how nested lists will be mapped. The nested lists are of type in both the objects.
#Mapping(target = "orderInvoice", source = "basePaymentRequest.invoice.eventPayload.orderInvoices")
ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);
Error
java: Can't map property "List<OrderInvoice> basePaymentRequest.invoice.eventPayload.orderInvoices" to "OrderInvoice orderInvoice". Consider to declare/implement a mapping method: "OrderInvoice map(List<OrderInvoice> value)".
I tried
#Mapping(target = "orderInvoice", expression= "java(basePaymentRequest.invoice.eventPayload.orderInvoices.get(0))")
But it gives error in Impl class
chargeTransaction.setOrderInvoice( basePaymentRequest.invoice.eventPayload.orderInvoices.get(0) );
java: incompatible types: com.sams.oms.ng.common.models.payment.request.OrderInvoice cannot be converted to com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice
IMHO the best way to solve this problem is to use a #Named paired with #Mapping#qualifiedByName
#Mapper
class Mapper {
#Mapping(target = "orderInvoice", source ="basePaymentRequest.invoice.eventPayload.orderInvoices", qualifiedByName="firstElement")
ChargeTransaction createInvoiceCTMapper(PaymentTriggerBaseModel basePaymentRequest, ChargeType chargeType);
#Named("firstElement")
OrderInvoice map(List<OrderInvoice> value) {
if(value == null) return null;
if(value.isEmpty()) return null;
return map(value.get(0));
}
abstract com.sams.oms.ng.common.models.payment.request.OrderInvoice map(com.sams.oms.ng.common.models.payment.cosmos.OrderInvoice invoice);
}
In this way you are instructed MapStruct to use map(List<>) to convert invoices to a single OrderInvoice and abstract map(OrderInvoice) to let MapStruct autogenerate mapping code.
Code in untested because I haven't limited spare time today,but I hope my example may be useful;if anything is wrong feel free to comment and I will correct code asap.

Map a list of object to another list using Dozer's custom converters

What I am trying to do is to map a List of entities to a list of their String ids (more or less) using Dozer.
Obviously, it implies Custom Converter. My first idea was to make a converter from MyEntity to a String, and then say to Dozer something like "Map every object of this collection using this converter". But I couldn't figure out how to do so.
So my second idea was to make a converter form a list of entities to a list of string, directly. My problem on this idea is that I was strugling on something ridiculous which is to get the type of my list in the constructor, as below (which doesn't work at all):
public MyEntityListConverter() {
super(List<MyEntity>.class, List<String>.class);
}
I don't know how to pass an instantiated list's class in a single row wihout declaring anything.
So if someone know either :
How to specify to dozer an object convertor to use in collection mapping
How to get instantiated list type
A third/better solution to try
The way you tried is not possible due to generic types. And if it was, Dozer cannot detect types at runtime.
1st solution with List<>
Your converter :
public class MyEntityToStringConverter extends DozerConverter<MyEntity, String> {
// TODO constructor + impl
}
Your mapping :
mapping(MyEntityA.class, MyEntityB.class)
.fields("myEntityList", "myStringList",
hintA(MyEntity.class),
hintB(String.class));
mapping(MyEntity.class, String.class)
.fields(this_(), this_(), customConverter(MyEntityToStringConverter.class));
2nd solution with list wrappers
You can try to create your custom classes extending a list impl.
public class MyEntityList extends ArrayList<MyEntity> {
}
public class MyStringList extends ArrayList<String> {
}
Change your field in the parent classes you want to map.
Your converter :
public class MyEntityToStringConverter extends DozerConverter<MyEntityList, MyStringList> {
// TODO constructor + impl
}
Your mapping :
mapping(MyEntityA.class, MyEntityB.class)
.fields("myEntityList", "myStringList", customConverter(MyEntityToStringConverter.class));
Another option would be
super((Class<List<MyEntity>>) (Class<?>) List.class,(Class<List<String>>) (Class<?>) List.class);
I very much inclined to #Ludovic solution, but there might be a catch as mentioned in my comment up there.
But a slight tweak works for me though - register the custom converter in "configuration" rather than field level. I'm using XML config but it should work with coding config:
<configuration>
<custom-converters>
<converter type="f.q.c.n.MyEntityToStringConverter">
<class-a>java.lang.String</class-a>
<class-b>f.q.c.n.MyEntity</class-b>
</converter>
</custom-converters>
</configuration>

The Method values() is undefined for the type String Error

I am currently experimenting in Spring MVC, and right now I'm trying to create a class of constants to populate a drop down list. The enum method is fine, but in my controller I get an error
"The method values() is undefined for the type String.
List<Job> jobList = new ArrayList<job>(
Arrays.asList(job.values()));
mav.addObject("jobList", jobList);
The error appears directly underneath .values() I have imported java.util.Enumeration so it's not that. Any ideas what it could be?
You want the values of the enum class Job, not call the values method on the job variable which in your case seems to be of type String.
Try:
List<Job> jobList = Arrays.asList(Job.values());

How to change Jackson's default concrete map type?

I have a very simple Jackson code to deserialize a nested JSON object:
public class MapTest
{
public static void main(String ... args) throws Exception
{
final String ser = "{\"nested\":{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":\"v3\"}}";
final Map<String, Object> deser = new ObjectMapper().readValue(ser, new TypeReference<TreeMap<String, Object>>(){});
System.out.println("Class of deser's nested object is " + deser.get("nested").getClass().getSimpleName());
}
}
When I run this I obtain the following output:
Class of deser's nested object is LinkedHashMap
However I want the nested map to be deserialized as a TreeMap rather than as a LinkedHashMap as per the output. How can I tell Jackson to default to use a TreeMap when deserializing?
Reading through the documentation the closest thing I found was the ability to define concrete classes for abstract types through addAbstractTypeMapping() in a module but I've tried every superclass and instance of LinkedHashMap in an attempt to do this and nothing seems to work.
This is using Jackson 2.4.2, although if there is a way to do this that requires a higher version I would be able to upgrade.
Module's addAbstractTypeMapping() is indeed the way to achive mapping in general. But the problem may be due to recursive nature of deserialization; because inner values are considered to be of type java.lang.Object.
However, I think there were indeed fixes to this part in 2.5, so I would specifically checking to see if 2.5.0 would work, once you add abstract mapping from Map to TreeMap.
If this does not work, please file a bug at https://github.com/FasterXML/jackson-databind/issues since it should work.

Play-Framework: Causing a 'IllegalArgumentException occured

The error I get is 'IllegalArgumentException occured' Can not set java.util.ArrayList field mi.types.ListOfObjects.objects to java.util.LinkedList
I'm doing the following...
ListOfObjects objects = li.getUsersObjects();
which works perfectly fine normally, however when I do the exact same call with the exact same code inside of Play it doesn't. I'm calling it in my Security Controller inside the authenticate() function. There's a case where the users objects are gotten from an external server. That's when this call is made. It works fine without Play and I'm pretty sure worked before I put it in the Security Controller. Why would putting it here cause such a problem?
EDIT
ListOfObjects is a custom object which contains some values.. It's located inside li which is a library I'm importing. It looks something like this...
public class ListOfObjects {
private ArrayList<Object> objects;
public ArrayList<Object> getObjects(){
return objects;
}
}
getUserObjects simply returns the objects for that user. It grabs them from a server and then uses gson to parse them. However I'm getting the above error when I attempt to.
EDIT2
No matter what I do my type for getUserObjects seems to always be returning as a LinkedList even though it's an ArrayList object. I've tried calling getObjects() directly and there's no change.
EDIT 3
getUserObjects is defined as...
Gson gson = new Gson();
ListOfObjects objects = gson.fromJson(r.getBody(), ListOfObjects.class);
StackTrace...
where r.getBody() is the JSON response from the server
Execution exception (In /app/controllers/Security.java around line 57)
IllegalArgumentException occured : Can not set java.util.ArrayList field lm.types.ListOfObjects.objects to java.util.LinkedList
play.exceptions.JavaExecutionException: Can not set java.util.ArrayList field lm.types.ListOfObjects.objects to java.util.LinkedList
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:227)
at Invocation.HTTP Request(Play!)
Caused by: java.lang.IllegalArgumentException: Can not set java.util.ArrayList field lm.types.ListOfObjects.objects to java.util.LinkedList
at com.google.gson.FieldAttributes.set(FieldAttributes.java:188)
at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler(JsonObjectDeserializationVisitor.java:118)
at com.google.gson.ObjectNavigator.navigateClassFields(ObjectNavigator.java:158)
at com.google.gson.ObjectNavigator.accept(ObjectNavigator.java:131)
at com.google.gson.JsonDeserializationContextDefault.fromJsonObject(JsonDeserializationContextDefault.java:73)
at com.google.gson.JsonDeserializationContextDefault.deserialize(JsonDeserializationContextDefault.java:51)
at com.google.gson.Gson.fromJson(Gson.java:568)
at com.google.gson.Gson.fromJson(Gson.java:515)
at com.google.gson.Gson.fromJson(Gson.java:484)
at com.google.gson.Gson.fromJson(Gson.java:434)
at com.google.gson.Gson.fromJson(Gson.java:406)
at lm.lib.LMethods.getUsersObjects(LMethods.java:165)
at controllers.Security.createNewUser(Security.java:57)
at controllers.Security.authenticate(Security.java:36)
at play.utils.Java.invokeStaticOrParent(Java.java:159)
at controllers.Secure$Security.invoke(Secure.java:193)
at controllers.Secure$Security.access$0(Secure.java:184)
at controllers.Secure.authenticate(Secure.java:64)
at play.mvc.ActionInvoker.invokeWithContinuation(ActionInvoker.java:540)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:498)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:474)
at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:469)
at play.mvc.ActionInvoker.invoke(ActionInvoker.java:157)
... 1 more
Debugger failed to attach: recv failed during handshake: Connection reset by peer
Exception message clearly says that Gson deserializes list as LinkedList, whereas field of your ListOfObjects is declared as ArrayList.
The general rule to avoid this kind of problems is not to use implementation classes in field declarations. Use List instead:
public class ListOfObjects {
private List<Object> objects;
...
}

Categories