snack - JsonPath 扩展定制
1、查询上下文的关键属性(与定制相关)
| 属性 | 描述 | 备注 |
|---|---|---|
isMultiple | 是否为多节点输出 | 前面执行过 ..x 或 * 或 [?]。通过 fun() 聚合后重置为 false |
isExpanded | 是否已展开 | 前面执行过 ..x 或 * |
isDescendant | 是否有后代 | 前面执行过 ..x |
查询时,接口输入的是一个节点(内部会变成一个单节点的节点列表),通过执行过 ..x(展开后代) 或 *(展开子代) 或 [?](用子代过滤) 片段后,会变成多节点的节点列表。即 isMultiple==true。
使用 ..x 或 * 时,会展开后代或子代。即 isExpanded==true。
使用 ..x 时,会展开后代,即有后代了 isDescendant==true。
2、操作符定制参考
示例:
public class OperatorDemo {
public static void main(String[] args) {
//::定制操作符(已预置)
OperatorLib.register("startsWith", (ctx, node, term) -> {
ONode leftNode = term.getLeftNode(ctx, node);
if (leftNode.isString()) {
ONode rightNode = term.getRightNode(ctx, node);
if (rightNode.isNull()) {
return false;
}
return leftNode.getString().startsWith(rightNode.getString());
}
return false;
});
//::检验效果
assert ONode.ofJson("{'list':['a','b','c']}")
.select("$.list[?@ startsWith 'a']")
.size() == 1;
}
}
了解接口 Operator 接口和参数:
package org.noear.snack4.jsonpath;
import org.noear.snack4.ONode;
import org.noear.snack4.jsonpath.filter.Term;
@FunctionalInterface
public interface Operator {
/**
* 应用
*
* @param ctx 查询上下文
* @param node 目标节点
* @param term 逻辑表达式项
*/
boolean apply(QueryContext ctx, ONode node, Term term);
}
| 参数 | 描述 |
|---|---|
| ctx | 查询上下文 |
| node | 当前节点 |
| term | 逻辑项描述(左操作元,操作符?,右操作元?)。同时支持一元、二元、三元操作的描述 |
3、扩展函数定制参考:
函数(或扩展函数)有两种用途(使用同一个接口开发):
- 过滤函数,用在过滤器中。
- 聚合函数,用在选择器中。
示例(本示例定制的函数可作: 过滤函数 或 聚合函数 使用):
package org.noear.snack4.jsonpath;
import org.noear.snack4.ONode;
import org.noear.snack4.jsonpath.FunctionLib;
import org.noear.snack4.jsonpath.JsonPathException;
public class FunctionDemo {
public static void main(String[] args) {
//定制 length 函数(已预置)
FunctionLib.register("length", (ctx, argNodes) -> {
if (argNodes.size() != 1) {
throw new JsonPathException("Requires 1 parameters");
}
ONode arg0 = argNodes.get(0); //节点列表(选择器的结果)
if (ctx.isMultiple()) {
return ctx.newNode(arg0.getArray().size());
} else {
if (arg0.getArray().size() > 0) {
ONode n1 = arg0.get(0);
if (n1.isArray()) return ctx.newNode(n1.getArray().size());
if (n1.isObject()) return ctx.newNode(n1.getObject().size());
if (ctx.hasFeature(Feature.JsonPath_JaywayMode) == false) {
if (n1.isString()) return ctx.newNode(n1.getString().length());
}
}
return ctx.newNode();
}
});
//检验效果//out: 3
System.out.println(ONode.ofJson("[1,2,3]")
.select("$.length()")
.toJson());
}
}
了解接口 Function 接口和参数:
package org.noear.snack4.jsonpath;
import org.noear.snack4.ONode;
import java.util.List;
@FunctionalInterface
public interface Function {
/**
* 应用
*
* @param ctx 查询上下文
* @param argNodes 参数节点列表
*/
ONode apply(QueryContext ctx, List<ONode> argNodes);
}
| 参数 | 描述 |
|---|---|
| ctx | 查询上下文 |
| argNodes | 参数节点列表 |
3、查询上下文 QueryContext
QueryContext 对整个查询定制工作极为重要:hasFeature 可以检查特性?getMode 可以知道当前是什么模式(查询,生成,删除)?等。
public interface QueryContext {
/**
* 有使用标准?
*/
boolean hasFeature(Feature feature);
/**
* 是否为多输出
*/
boolean isMultiple();
/**
* 是否为已展开
*/
boolean isExpanded();
/**
* 是否有后代选择
*/
boolean isDescendant();
/**
* 查询根节点
*/
ONode getRoot();
/**
* 查询模式
*/
QueryMode getMode();
/**
* 获取根选项配置
*/
Options getOptions();
/**
* 获取节点的子项
*/
ONode getChildNodeBy(ONode node, String key);
/**
* 获取节点的子项
*/
ONode getChildNodeAt(ONode node, int idx);
/**
* 缓存获取
*/
<T> T cacheIfAbsent(String key, Function<String, ?> mappingFunction);
/**
* 内嵌查询(`@.user.name`)
*/
QueryResult nestedQuery(ONode target, JsonPath query);
/**
* 新建节点
*/
default ONode newNode() {
return new ONode(getOptions());
}
/**
* 新建节点
*/
default ONode newNode(Object value) {
return new ONode(getOptions(), value);
}
}