I have a payload like
String payload = "{\"array\":[{\"a\":\"b\",\"c\":10},{\"a\":\"b\",\"c\":20},{\"a\":\"b\",\"c\":30}],\"boolean\":true,\"color\":\"gold\",\"null\":null,\"number\":123,\"object\":{\"a\":\"b\",\"c\":\"d\"},\"string\":\"HelloWorld\"}";
This string payload is mapped as a HashMap using
Map map = new ObjectMapper().readValue(payload, HashMap.class);
Now, I want to add a functionality that enables me to get value and perform some operations using just a String Expression
passed.
For Eg:
doSomething(data, "data.array")
// Output :[{\"a\":\"b\",\"c\":10},{\"a\":\"b\",\"c\":20},{\"a\":\"b\",\"c\":30}]
-> as an ObjectdoSomething(data, "data.array.stream().map(item -> item.get('c')).sum()")
// Output :10 20 30 = 60
I want to achieve something like this. Can someone pls help, how can I implement this.
CodePudding user response:
SpEL cannot understand lambda expressions (->).
You can use streams but without lambdas.
follow this answer for a detailed explanation: https://stackoverflow.com/a/48842973/7972621
CodePudding user response:
Library Josson has the capability to get value and perform some operations using just a String Expression
passed.
https://github.com/octomix/josson
Deserialization
String payload = "{\"array\":[{\"a\":\"b\",\"c\":10},{\"a\":\"b\",\"c\":20},{\"a\":\"b\",\"c\":30}],\"boolean\":true,\"color\":\"gold\",\"null\":null,\"number\":123,\"object\":{\"a\":\"b\",\"c\":\"d\"},\"string\":\"HelloWorld\"}";
Josson data = Josson.fromJsonString(payload);
Query an array node
JsonNode node = data.getNode("array");
System.out.println(node.toPrettyString());
Output
[ {
"a" : "b",
"c" : 10
}, {
"a" : "b",
"c" : 20
}, {
"a" : "b",
"c" : 30
} ]
Evaluate data by a function
JsonNode node = data.getNode("array.c.sum()");
System.out.println(node.toPrettyString());
Output
60.0
CodePudding user response:
SPeL
cannot understand lambda expressions (->), but there is an implicit method.
You can register and use lambdas as SPeL
#functions.
You can extend SPeL
by registering user defined functions that can be called within the expression string. The function is registered with the StandardEvaluationContext
using the method.
We can write an example that takes the SPeL
code directly or contains the StandardEvaluationContext
:
public class SPeLExample {
public static void main(String[] args) {
try {
// JSON payload.
String payload = "...";
// Response 1 test.
List<HashMap<String, Object>> response1 = (List<HashMap<String, Object>>)
doSomething(payload, "#root['array']");
System.out.println("Response 1 => " response1);
// Response 2 test.
StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
standardEvaluationContext.registerFunction("getMapItem",
SPeLExample.class.getMethod("getMapItem", String.class));
standardEvaluationContext.registerFunction("toInteger",
SPeLExample.class.getMethod("toInteger"));
Integer response2 = (Integer) doSomething(payload,
"#root['array'] == null ? null "
": #root['array'].stream()"
" .map(#getMapItem('c'))"
" .mapToInt(#toInteger()).sum()",
standardEvaluationContext);
System.out.println("Response => " response2);
} catch (Exception e) { throw new RuntimeException(e); }
}
public static Object doSomething(String payload,
String expressionString) {
return doSomething(payload, expressionString, null);
}
public static Object doSomething(String payload,
String expressionString,
StandardEvaluationContext standardEvaluationContext) {
try {
Map<String, Object> map = new ObjectMapper()
.readValue(payload, HashMap.class);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression(expressionString);
if (standardEvaluationContext == null)
return expression.getValue(map);
return expression.getValue(standardEvaluationContext, map);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static Function<LinkedHashMap, Object> getMapItem(String key) {
return o -> o.get(key);
}
public static ToIntFunction<Object> toInteger() {
return o -> Integer.valueOf((int)o);
}
}
I added two lambda functions getMapItem
and toInteger
, I registered them in StandardEvaluationContext
.
I rewrote your "data.array.stream().map(item -> item.get('c')).sum()"
code to handle special functions like this:
"#root['array'] == null ? null : #root['array'].stream().map(#getMapItem('c')).mapToInt(#toInteger()).sum()"