在嵌套在Hashmap中的数组上实现函数,以使用Spel Spring执行聚合操作

20jt8wwn  于 2022-10-04  发布在  Spring
关注(0)|答案(3)|浏览(188)

我有像这样的有效载荷

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"}";

使用将该字符串有效负载Map为HashMap

Map map = new ObjectMapper().readValue(payload, HashMap.class);

现在,我想添加一个功能,使我能够仅使用传递的String Expression获取值并执行一些操作。

例如:

doSomething(data, "data.array")//输出:[{\"a\":\"b\",\"c\":10},{\"a\":\"b\",\"c\":20},{\"a\":\"b\",\"c\":30}]->作为对象
doSomething(data, "data.array.stream().map(item -> item.get('c')).sum()")//输出:10 + 20 + 30 = 60

我想要实现这样的目标。有没有人能帮帮忙,我怎么才能实现这个目标。

jei2mxaa

jei2mxaa1#

Spel无法理解lambda表达式(->)。

您可以使用Streams,但不使用lambdas

按照以下答案获得详细解释:https://stackoverflow.com/a/48842973/7972621

e5nqia27

e5nqia272#

Josson仅使用传递的String Expression即可获取值并执行某些操作。

https://github.com/octomix/josson

反序列化

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);

查询数组节点

JsonNode node = data.getNode("array");
System.out.println(node.toPrettyString());

输出

[ {
  "a" : "b",
  "c" : 10
}, {
  "a" : "b",
  "c" : 20
}, {
  "a" : "b",
  "c" : 30
} ]

通过函数计算数据

JsonNode node = data.getNode("array.c.sum()");
System.out.println(node.toPrettyString());

输出

60.0
xriantvc

xriantvc3#

SPeL无法理解lambda表达式(->),但有一个隐式方法。

您可以注册并使用lambdas作为SPeL #functions

您可以通过注册可在表达式字符串中调用的用户定义函数来扩展SPeL。使用该方法将该函数注册到StandardEvaluationContext

我们可以编写一个直接获取SPeL代码或包含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);
    }
}

我添加了两个lambda函数getMapItemtoInteger,并将它们注册到StandardEvaluationContext中。

我重写了您的"data.array.stream().map(item -> item.get('c')).sum()"代码以处理如下所示的特殊函数:

"#root['array'] == null ? null : #root['array'].stream().map(#getMapItem('c')).mapToInt(#toInteger()).sum()"

相关问题