SpEL Cannot Be Cast to Map - java

Having read the documentation on "Inline Maps" for Spring Expression Language, I am passing an object to a SpelExpressionParser and .toString()ing the Object, so to speak; however, in doing so, I am receiving the following error:
org.springframework.expression.spel.standard.SpelExpression cannot be
cast to java.util.Map
The Object passed to as the argument to the .parseExpression function is the result of the annotation #PreAuthorize("hasPermission(#object, {name: 'roofus', animal: 'dog'}) "affixed" to a method.
Through the PermissionEvaluator interface implementation, it is passed in its Object form to a method:
private boolean doSomething (Object animal) { //....
Within this method is found an #Autowired SpelExpressionEvaluator. This is used in the following way:
Map animalMap = (Map) parser.parseExpression(animal.toString());
Through debugging, I know that the .toString() method results in: {name=roofus, animal=dog}
Resulting in the aforementioned error. Am I missing something? The goal is to be able to pass in a "JSON"-esque String (as specified by the linked documentation) for evaluation purposes.

Your problem that parser.parseExpression can't return Map independently of circumstances .
Looks like you misunderstood the architecture a bit. The SpelParser is for building Expression object from the the String. After that you can evaluate that expression using one of its getValue() method.
So, only after the evaluation you can get your Map object:
Expression expression = parser.parseExpression("{name: 'roofus', animal: 'dog'}");
Map map = expression.getValue(Map.class);

animal is already a Map (notice the formatting in the OP of the .toString() result) and not in the correct format expected by the parser. In this case, you don't even need the parser if the objective is to retrieve information from the Map:
((Map<String, String>)animal).get("animal")
So, in response to the solution proposed by Artem Bilan, this would actually work:
Map animalMap = (Map) parser.parseExpression("{name: 'roofus', animal: 'dog'}").getValue();
However, again, what is actually received (and the cause of the problem in the OP) is: parser.parseExpression("{name=roofus, animal=dog}")

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.

Use CriteriaBuilder to filter IN multiple ENUM values

I want to construct a query with CriteriaBuilder, and to add a Predicate into the where instruction, to filter one of my object field from a potential list of ENUM values.
Despite this similar post : Filtering data with CriteriaBuilder to compare enum values with literals not working
I didn't managed to make my code works. Here is the "simplified" object:
//BUNCH OF ANNOTATIONS
public class Action {
#Column(name = "CONTEXT")
#Enumerated(EnumType.STRING)
private ActionContext context = ActionContext.SALE;
// ...
}
My enum is:
public enum ActionContext {
SALE,
ORDER,
OTHER
}
And in my filters, I can for example receive something like "SALE,ORDER".
So I created a custom Specification, and when I construct it, I'm doing this :
private List<Predicate> filters = new ArrayList<>();
//...
// filterValue is a String, it can be "SALE,ORDER" for ex.
case CONTEXT_FILTER_NAME:
if (filterValue != null && !filterValue.isEmpty()) {
String[] contextTokens = filterValue.split(",");
CriteriaBuilder.In<String> inClause = cb.in(root.get(CONTEXT_FILTER_NAME));
Arrays.asList(contextTokens).forEach(inClause::value);
filters.add(inClause);
}
break;
The part after is just doing the query, it works for other filters ... but the fact my entity have an enum : when I run a test with these filters (sending "SALE,ORDER" filter for ex), I get the following error :
Parameter value [SALE] did not match expected type
[com.mycompany.domain.enums.ActionContext
I also tried by replacing CriteriaBuilder.in by .or() and putting in the collection of parsed tokens, but I had the same error.
And yes, I'm sure it's not a parsing error, because when I put a breakpoint in this swich/case section, I see the correctly constructed contextTokens array with good value in it. And in the Specification 'in' I can see the correct values being set.
Does anybody know what I'm missing ? thx a lot
OK I was comparing Enum with String, and I didn't see this ...
So you just have in this cases to convert ActionContext ENUM with ActionContext.valueOf(token) or on the other way around, converting the root.get(CONTEXT_FILTER_NAME).as(String.class).
Hope it helps people, that are like me, sometimes in a kind of tunnel vision.

Using scala.collection.immutable.Map inside my java class

I get bellow exception from my java codes.
java.lang.ClassCastException: scala.collection.immutable.Map$Map1 cannot be cast to java.util.HashMap
at au.com.vroc.udf.medianUDF.update(medianUDF.java:79)
I am getting error in my spark application when I cast the buffer to HashMap of java.utill. This is my codes:
public void update(MutableAggregationBuffer buffer, Row input) {
if (!input.isNullAt(0)) {
HashMap currentBuffer=(HashMap) buffer.get(0);//getting exception here
//HashMap currentBuffer=new HashMap();
currentBuffer.put(input.getLong(0), input.getDouble(0));
//currentBuffer.add(currentMap);
buffer.update(0, currentBuffer);
}
}
I guess instead of java hashmap I have to use "scala.collection.immutable.Map$Map1" inside my java class. Can I use any tool in "JavaConversions" namespace.
Anyhep would be appreciated!
Simplest approach would likely be to use Scala Converters.
It should look something like this (not tested, but type-checks):
import scala.collections.JavaConverters
java.util.Map currentBuffer = JavaConverters.mapAsJavaMapConverter(buffer.get(0)).asJava();
Please note that it returns type-parameterized map (i.e. java.util.Map<K, V>), not the non-parameterized java.util.HashMapin your example - you might want to alter the rest of your code to work on the parameterized maps for better type safety.
You get java.util.Map you should use getJavaMap method:
java.util.Map<T, U> currentBuffer = (java.util.Map<T, U>) first.getJavaMap(0)
Note that this is not HashMap - initialized value is Encoded on update and decoded on get. To modify it, you have to make a copy.

java reflections list NoSuchMethodException

I'm currently working on a commandline-client for automated testing. For this, I want to be able to perform a request just by a given name, so I have to use reflection.
However, I get the following error:
NoSuchMethodException: java.util.ArrayList.add(some.path.Foo)
on this code:
Object job = Class.forName(sClassName).getConstructor().newInstance();
List<?> jobObject = (List<?>) Request.getClass().getDeclaredMethod(sMethodName).invoke(Request);
jobObject.getClass().getDeclaredMethod("add", job.getClass()).invoke(Request, job);
On the other hand, the following code works (but is not the solution as Foo should be dynamic, not hardcoded)
Object job = Class.forName(sClassName).getConstructor().newInstance();
List<Foo> jobObject = (List<Foo>) Request.getClass().getDeclaredMethod(sMethodName)).invoke(Request);
jobObject.add((Foo)job);
Sidenote:
sClassName = some.path.Foo
.getDeclaredMethod(sMethod).invoke(Request) returns an Object (which is a List< Foo>)
What I want to achieve is to get the code working with dynamic inputs (for example Bar instead of Foo)
Due to type erasure, the method add of a List expects at runtime an object of type Object (in other words its signature is boolean add(Object o)) moreover you need to invoke it on jobObject not on Request as it is not a List, so your code should rather be:
jobObject.getClass().getDeclaredMethod("add", Object.class).invoke(jobObject, job);
Unless I miss something for me you don't even need reflection in this case
List<Object> jobObject = (List<Object>)...
jobObject.add(job);

JUnit assertThat: check that Object equals String

I have Map declared as following:
Map<String, Object> data
I put a String in it and verify its value like this:
assertEquals("value", data.get("key"));
Now, I'd like to rewrite the verification to use assertThat instead of assertEquals. I've tried the following:
assertThat(data.get("key"), equalTo("value"));
And of course it didn't work because of type mismatch:
Wrong 2nd argument type. Found: 'org.hamcrest.Matcher<java.lang.String>', required: 'org.hamcrest.Matcher<? super java.lang.Object>' less...
Explicit type cast of the first argument to String helps, but I'd like to avoid it. For example assertEquals doesn't require type cast.
So, how can I check that the value, which was put into Map object, declared above, is equal to particular String, using the assertThat method?
The "more assertThat" way of doing things would be:
Map<String, Object> expectedData = Collections.singletonMap("key", "value");
asssertThat(data, is(expectedData));
Please note:
Maybe you need type hints for the call to singletonMap
Besides the is matcher, there are other matchers that would allow you to check that data contains your "expected" map data
For your specific problem: that is caused because how generics come into play here; it might be sufficient to use (String) data.get("key") - to tell the compiler that the "actual" argument is of type String.
In the end - I have no idea what your problem is. I wrote down this piece of code:
public void test() {
Map<String, Object> data = new HashMap<>();
data.put("key", "value");
assertThat(data.get("key"), is("value"));
Map<String, Object> expectedData = Collections.singletonMap("key", "value");
assertThat(data, is(expectedData));
}
It compiles fine, and the unit test runs and passes. In other words: actually I am unable to repro your problem.
try this
assertThat(data.get("key"), equalTo("value"))
or
assertThat(data.get("key"), CoreMatchers.equalTo("value"))

Categories