从JsonPath和XPath到SPL

XML和Json不仅是结构化文本,而且擅长表示多层数据,可承载足够通用和足够丰富的信息,因此常被用于各种数据交换和信息传递事务,比如WebService/Restful,微服务等 。但多层数据要比传统的二维表结构复杂,取数后再处理的难度也大 。
早期,没有专业的json/XML的后处理技术,JAVA开发者通常要采取硬写代码或入库再算的方式 。硬编码计算能力差,代码量大,开发效率很低 。入库虽然解决了部分计算能力,但步骤多,延迟大,额外制造了JAVA与数据库的紧耦合,架构性很差,而且数据库只擅长计算二维表,处理多层结构化数据的能力并不强 。后来,专业的json/XML后处理技术开始出现,才使Java中做这些运算处理的效率有了较大的提升,JsonPath/XPath是其中的优秀者 。
JsonPath和XPath具有突破性的计算能力XPath是广泛使用的XML处理语言,内置于XOM/Xerces-J/Jdom/Dom4J等函数库 。JsonPath仿照XPath的语法,实现了类似的功能,且有自己的函数库,目前是广泛使用的Json处理语言 。比起以前硬编码的方式,XPath/JsonPath代码简短得多,具有突破性的计算能力 。
比如,用arronlong HTTP函数库从WebService取XML字符串,使用Dom4J函数库将XML字符串解析为Document类型,使用Dom4J内置的XPath语法进行条件查询:
 String path= "http://.../emp_orders.xml";String XMLStr= com.arronlong.httpclientutil.HttpClientUtil.get(com.arronlong.httpclientutil.common.HttpConfig.custom().url(path));Document doc = DocumentHelper.parseText(XMLStr);List<Node> list=doc.selectNodes("/xml/row/Orders[Amount>1000 and Amount<=3000 and contains(Client,'bro')]")类似地,用 JsonPath 进行条件查询:
 String path= "http://.../emp_orders.json";String JsonStr= com.arronlong.httpclientutil.HttpClientUtil.get(com.arronlong.httpclientutil.common.HttpConfig.custom().url(path));Object document = Configuration.defaultConfiguration().jsonProvider().parse(JsonStr);ArrayList l=JsonPath.read(document, "$[*].Orders[?(@.Amount>1000 && @.Amount<2000 && @.Client =~ /.*?business.*?/i )]");JsonPath与XPath用法类似,语法相通,计算能力差别不大,下面以JsonPath为主进行说明 。JsonPath/XPath对条件查询的支持比较完整,包括关系运算符,如大于、小于等于;逻辑运算符,如与、或、非;字符串正则表达式,如~ /.*?business.*?/i;字符串函数,如模糊匹配contains 。此外,JsonPath/XPath还支持在条件查询中使用数学运算符(函数),如+-*、div;位置函数,如position、last;日期函数,如year-from-date、timezone-from-time 。
需要特别说明的是,JsonPath/XPath可以灵活表达条件查询的层级范围,包括绝对位置、相对位置、父节点、子节点、属性、元素等,这是多层数据处理语言有别于二维数据处理语言(SQL)之处,如代码中的$[*].Orders和/xml/row/Orders 。
除了条件查询,JsonPath/XPath还支持聚合计算,比如用JsonPath求和:
Double d=JsonPath.read(document, "$.sum($[*].Orders[*].Amount)");
JsonPath/XPath还支持平均、最大、最小、计数等聚合函数 。
从这些例子可以看出来,JsonPath/XPath的语法直观易懂,可以用较短的代码实现条件查询和聚合计算,可以方便地访问多层结构,比硬编码方便多了 。
JsonPath和XPath计算能力仍然不足比起直接用Java编码,JsonPath和XPath的计算能力的确是突破性的,但要进行日常计算甚至是基础计算,JsonPath和XPath的能力是严重不足的,远不如SQL 。事实上,JsonPath/XPath只支持条件查询和聚合这两种最基本的计算,其他计算都要用复杂的编码辅助完成 。
比如,用JsonPath进行分组汇总:
ArrayList orders=JsonPath.read(document, "$[*].Orders[*]");Comparator<HashMap> comparator = new Comparator<HashMap>() {public int compare(HashMap record1, HashMap record2) {if (!record1.get("Client").equals(record2.get("Client"))) {return ((String)record1.get("Client")).compareTo((String)record2.get("Client"));} else {return ((Integer)record1.get("OrderID")).compareTo((Integer)record2.get("OrderID"));}}};Collections.sort(orders, comparator);ArrayList<HashMap> result=new ArrayList<HashMap>();HashMap currentGroup=(HashMap)orders.get(0);double sumValue=https://www.isolves.com/it/cxkf/qd/2022-07-26/(double) currentGroup.get("Amount");for(int i = 1;i < orders.size(); i ++){HashMap thisRecord=(HashMap)orders.get(i);if(thisRecord.get("Client").equals(currentGroup.get("Client"))){sumValue=sumValue+(double)thisRecord.get("Amount");}else{HashMap newGroup=new HashMap();newGroup.put(currentGroup.get("Client"),sumValue);result.add(newGroup);currentGroup=thisRecord;sumValue=(double) currentGroup.get("Amount");}}


推荐阅读