计算处理 Json 的 Java 开源库 esProc SPL
esProc SPL是强大的开源计算引擎,可以方便地处理json相关的运算。
下载、安装、集成
源代码在这里:github.com/SPLWare/esProc,不过从源码编译比较麻烦,官方提供了已经编译好的安装包,可以从c.raqsoft.com.cn/article/1595816810031下载,如果这个链接失效,则可以搜索esProc SPL找到官网下载。
安装之后,在 [安装目录]\esProc\lib下可以找到Java集成需要的3个jar包:
esproc-bin-xxxx.jar esProc SPL及其JDBC驱动包
icu4j_60.3.jar 处理国际化
jdom-1.1.3.jar 解析配置文件
将上述jar包引入自己的Java项目,就可以通过JDBC接口执行SPL代码了:
Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("=1");
result.next();
int IntResult=results.getInt(1);
上述代码能正常执行,并且在 IntResult 中可以提到结果 1,就说明集成成功了。
基本Json运算
现在就可以用SPL处理Json了。
先试个简单的情况。strJson是Java里的Json字符串变量,部分内容:
[
{"OrderID":32,"Client":"JFS","SellerId":3,"Amount":468.0,"OrderDate":"2009-08-13"}
{"OrderID": 70,"Client": "DSG","SellerId": 7,"Amount": 288,"OrderDate": "2009-09-30"},
{"OrderID": 131,"Client": "FOL","SellerId": 7,"Amount": 103.2,"OrderDate": "2009-12-10"},
...
]
Java 将该字符串传给 SPL,由 SPL 进行分组汇总:
PreparedStatement pstmt = connection.prepareStatement("=json(?).groups(Client;sum(Amount):amt, count(1):cnt)");
pstmt.setString(1,strJson);
ResultSet result =pstmt.executeQuery();
可以像常规调用数据库 SQL 一样,在语句中用? 传递参数,只不过用的语句不是 SQL 了。
查看返回的 result,可以看到计算结果如下:
Client amt cnt
ARO 899.0 1
BDR 4278.8 4
BON 2564.4 1
…
上面返回表格形式的ResultSet,也可以返回成Json串:
PreparedStatement pstmt = connection.prepareStatement("=json(json(?).groups(Client;sum(Amount):amt, count(1):cnt))");
pstmt.setString(1,strJson);
ResultSet result =pstmt.executeQuery();
result.next();
String strResult=results.getString(1);
计算结果:
[{“Client”:“HDR”,“amt”:21040.0,“cnt”:21},{“Client”:“IBM”,“amt”:19030,“cnt”:19},…]
来自文件的Json就更简单了。用SPL代码读同样内容的Json文件,并进行分组汇总:
statement.executeQuery("=json(file(\"d:/orders.json\").read()).groups(Client;sum(Amount):amt, count(1):cnt)");
可以得到同样的计算结果。
SPL支持丰富的字符串和日期函数。比如:过滤出年份为2021年,且客户名包含指定字符串的订单(后续只写SPL代码):
=json(file(\"d:/orders.json\").read()).conj(Orders).select(year(OrderDate)==2012 && like@c(Client,"*business*"))
更多的计算函数和语法可以去参考官网文档,大多数常规运算都只要一句就能搞定。
进阶Json计算
计算多层Json
json经常是多层的,比如这样一段json,上层是员工记录,下层是员工的订单记录:
[{
"EId": 7,"State": "Illinois","Dept": "Sales","Name": "Alexis","Gender": "F","Salary": 9000,"Birthday": "1972-08-16",
"Orders": [
{"OrderID": 70,"Client": "DSG","SellerId": 7,"Amount": 288,"OrderDate": "2009-09-30"},
{"OrderID": 131,"Client": "FOL","SellerId": 7,"Amount": 103.2,"OrderDate": "2009-12-10"}
]
}
{
"EId": 8,"State": "California",
...
}]
计算目标:Java 将该 json 串传入 SPL,SPL 解析 Json,合并下层订单,并进行过滤。
这里的计算步骤稍多,需要更专业的 SPL 开发和调试环境。执行 [安装目录]\esProc\bin\esproc.exe(Linux\Mac 下为 esproc.sh), 打开 SPL IDE,编辑以下脚本文件:
A |
|
1 |
=json(strJson) |
2 |
=A1.conj(Orders) |
3 |
=A2.select(Amount>arg1 && Amount<=arg2 && like@c(Client,"*business*")) |
在IDE中的样子:
代码中的 strJson、arg1、arg2 是参数,可以来自 Java JDBC 或报表工具。通过菜单 Program->Parameter 定义参数,如下:
注意:在 SPL 代码里,Json 串是常数,可以直接使用,但在 Java 代码里,Json 串是普通字符串,传入 SPL 后需要先用 json() 函数解析再使用。为了保持代码一致,在 IDE 里测试 Json 串参数时,需要在前面加单引号,以便模拟成普通字符串。
执行或调试脚本后,依次点击 A1-A3,可以在右侧看到每一步的计算结果:
在 Java 中调用 SPL 脚本:将上述 SPL 脚本存为文件 (比如 splFile.splx),再在 Java 中以存储过程的形式引用该文件名。
Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
CallableStatement statement = connection.prepareCall("{call splFile (?,?, ?)}");
statement.setString(1,strJson);
statement.setInt(2, 1000);
statement.setInt(3, 3000);
ResultSet result= statement.executeQuery();
Json 串经常来自 HTTP 数据源(比如 Restful),而不是文件。SPL 可以直接读 HTTP 数据源,这样就不必从 Java 中传入参数了。比如:
A |
|
1 |
=httpfile("http://127.0.0.1:6868/restful/emp_orders").read() |
2 |
=json(A1) |
3 |
=A2.conj(Orders) |
4 |
=A3.select(Amount>1000 && Amount<=3000 && like@c(Client,"*business*")) |
更详细的信息(比如访问安全性等问题)可以参考《SPL:HTTP/WebService/Restful 服务的访问》
SPL提供了外部库接口,用来支持更多的数据源,比如从Kafka取数并计算:
A |
|
1 |
=kafka_open("/kafka/my.properties", "topic1") |
2 |
=kafka_poll(A1) |
3 |
=A2.derive(json(value):v).new(key, v.fruit, v.weight) |
4 |
=stax_close(A1) |
还支持MongoDB的bson格式:
A |
|
1 |
=mongo_open("mongodb://127.0.0.1:27017/mongo") |
2 |
=mongo_shell(A1,"test1.find()") |
3 |
=A2.conj(Orders) |
4 |
=A3.select(Amount>1000 && Amount<=3000 && like@c(Client,"*business*")).fetch() |
5 |
=mongo_close(A1) |
外部库需要配置好才能使用,具体用法可以参考 外部库介绍
延伸阅读资料:从 jsonpath 和 xpath 到 SPL、开源 SPL,Webservice/Restful 的后处理利器。