🔥 全球首个支持 IETF JSONPath (RFC 9535) 标准的 Java 框架发布
基于jdk8。支持:Json Dom 的构建、编码解转换、获取、JsonPath 查询、JsonSchema 验证。
<dependency>
<groupId>org.noear</groupId>
<artifactId>snack4-jsonpath</artifactId>
<version>4.0.0</version>
</dependency>
Snack-Jsonpath 借鉴了 Javascript
所有变量由 var
申明,及 Xml dom
一切都是 Node
的设计。其下一切数据都以ONode
表示,ONode
也即 One node
之意,代表任何类型,也可以转换为任何类型。
- 强调文档树的构建和操控能力
- 高性能
Json path
查询(比 jayway.jsonpath 快很多倍)。同时兼容jayway.jsonpath
和 IETF JSONPath (RFC 9535) 标准 (用options
切换)。为下一个十年提供强劲的 JsonPath 体验。 - 支持
Json schema
架构校验 - 支持
json5
部分特性(无键字段,注释,等...) - 优先使用 无参构造函数 + 字段 编解码(可减少注入而触发动作的风险)
依赖包清单:
依赖包 | 描述 |
---|---|
org.noear:snack4 |
提供 json dom 构建和编解码支持 |
org.noear:snack4-jsonpath |
提供 json path 查询支持 |
org.noear:snack4-jsonschema |
提供 json schema 校验支持 |
开源项目仓库地址:
文档资料:
1、版本更新说明
- 重构整个项目(除了名字没变,其它都变了)
- 单测覆盖率 98%,历时小半年
- 支持
IETF JSONPath (RFC 9535)
标准(全球首个支持该标准的 Java 框架)。同时兼容jayway.jsonpath
- 添加 json-schema 支持
2、JSONPath 语法参考
语法元素 | 描述 |
---|---|
$ |
根节点标识符 |
@ |
当前节点标识符(仅在过滤选择器中有效) |
[<selectors>] |
子段:选择节点的零个或多个子节点 |
.name |
简写 ['name'] |
.* |
简写 [*] |
..[<selectors>] |
后代段:选择节点的零个或多个后代 |
..name |
简写 ..['name'] |
..* |
简写 ..[*] |
'name' |
名称选择器:选择对象的命名子对象 |
* |
通配符选择器:选择节点的所有子节点 |
3 |
索引选择器:选择数组的索引子项(从 0 开始) |
0💯5 |
数组切片选择器:数组的 start🔚step |
?<logical-expr> |
过滤选择器:使用逻辑表达式选择特定的子项 |
fun(@.foo) |
过滤函数:在过滤表达式中调用函数(IETF 标准) |
.fun() |
聚合函数:作为片段使用(jayway 风格) |
过滤选择器语法参考:
语法 | 描述 | 优先级 |
---|---|---|
(...) |
分组 | 5 |
name(...) |
函数扩展 | 5 |
! |
逻辑 非 |
4 |
== ,!= ,< ,<= ,> ,>= |
关系比较符 | 3 |
&& |
逻辑 与 |
2 |
\|\| |
逻辑 或 |
1 |
IETF JSONPath (RFC 9535) 标准定义操作符(支持)
操作符 | 描述 | 示例 |
---|---|---|
== |
左等于右(注意1不等于'1') | $[?(@.a == 1)] |
!= |
左不等于右 | $[?(@.a != 1)] |
< |
左比右小 | $[?(@.a < 1)] |
<= |
左小于或等于右 | $[?(@.a <= 1)] |
> |
左大于右 | $[?(@.a > 1)] |
>= |
左大于等于右 | $[?(@.a >= 1)] |
jayway.jsonpath 增量操作符(支持)
操作符 | 描述 | 示例 |
---|---|---|
=~ |
左匹配正则表达式 | [?(@.s =~ /foo.*?/i)] |
in |
左存在于右 | [?(@.s in ['S', 'M'])] |
nin |
左不存在于右 | |
subsetof |
左是右的子集 | [?(@.s subsetof ['S', 'M', 'L'])] |
anyof |
左与右有一个交点 | [?(@.s anyof ['M', 'L'])] |
noneof |
左与右没有交集 | [?(@.s noneof ['M', 'L'])] |
size |
左(数组或字符串)的大小应该与右匹配 | $[?(@.s size @.expected_size)] |
empty |
Left(数组或字符串)应该为空 | $[?(@.s empty false)] |
IETF JSONPath (RFC 9535) 标准定义函数(支持)
函数 | 描述 | 参数类型 | 结果类型 |
---|---|---|---|
length(x) |
字符串、数组或对象的长度 | 值 | 数值 |
count(x) |
节点列表的大小 | 节点列表 | 数值 |
match(x,y) |
正则表达式完全匹配 | 值,值 | 逻辑值 |
search(x,y) |
正则表达式子字符串匹配 | 值,值 | 逻辑值 |
value(x) |
节点列表中单个节点的值 | 节点列表 | 值 |
jayway.jsonpath 函数(支持)
函数 | 描述 | 输出类型 |
---|---|---|
length() |
字符串、数组或对象的长度 | Integer |
min() |
查找当前数值数组中的最小值 | Double |
max() |
查找当前数值数组中的最大值 | Double |
avg() |
计算当前数值数组中的平均值 | Double |
stddev() |
计算当前数值数组中的标准差 | Double |
sum() |
计算当前数值数组中的总和 | Double |
keys() |
计算当前对象的属性键集合 | Set<E> |
concat(X) |
将一个项或集合和当前数组连接成一个新数组 | like input |
append(X) |
将一个项或集合 追加到当前路径的输出数组中 | like input |
first() |
返回当前数组的第一个元素 | 依赖于数组元素类型 |
last() |
返回当前数组的最后一个元素 | 依赖于数组元素类型 |
index(X) |
返回当前数组中索引为X的元素。X可以是负数(从末尾开始计算) | 依赖于数组元素类型 |
snack-jsonpath 增量操作符(支持)
操作符 | 描述 | 示例 |
---|---|---|
startsWith |
左(字符串)开头匹配右 | [?(@.s startsWith 'a')] |
endsWith |
左(字符串)结尾匹配右 | [?(@.s endsWith 'b')] |
contains |
左(数组或字符串)包含匹配右 | [?(@.s contains 'c')] |
3、JSONPath 语法示例
JSON 样本数据
{ "store": {
"book": [
{ "category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{ "category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{ "category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{ "category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 399
}
}
}
示例JSONPath表达式及其应用于示例JSON值时的预期结果
JSONPath | 预期结果 |
---|---|
$.store.book[*].author |
书店里所有书的作者 |
$..autho |
所有作者 |
$.store.* |
商店里的所有东西,包括一些书和一辆红色的自行车 |
$.store..price |
商店里所有东西的价格 |
$..book[2] |
第三本书 |
$..book[2].author |
第三本书的作者 |
$..book[2].publisher |
空结果:第三本书没有“publisher”成员 |
$..book[-1] |
最后一本书 |
$..book[0,1]
$..book[:2] |
前两本书 |
$..book[?@.isbn] |
所有有国际标准书号的书 |
$..book[?@.price<10] |
所有比10便宜的书 |
$..* |
输入值中包含的所有成员值和数组元素 |
4、放几个应用示例看看
支持 dom
操控
ONode oNode = new ONode();
oNode.set("id", 1);
oNode.getOrNew("layout").then(o -> {
o.addNew().set("title", "开始").set("type", "start");
o.addNew().set("title", "结束").set("type", "end");
});
oNode.get("id").getInt();
oNode.get("layout").get(0).get("title").getString();
oNode.getOrNew("list").fillJson("[1,2,3,4,5,6]");
支持 json path
查询、构建、删除
ONode.ofBean(store).select("$..book[?@.tags contains 'war'].first()").toBean(Book.class); //RFC9535 规范,可以没有括号
ONode.ofBean(store).select("$..book[?(!(@.category == 'fiction') && @.price < 40)].first()").toBean(Book.class);
ONode.ofJson(store).select("$.store.book.count()");
ONode.ofBean(store).create("$.store.book[0].category").toJson();
ONode.ofBean(store).delete("$..book[-1]");
支持 json schema
校验
JsonSchema schema = JsonSchema.ofJson("{type:'object',properties:{userId:{type:'string'}}}"); //加载架构定义
schema.validate(ONode.load("{userId:'1'}")); //校验格式
支持序列化、反序列化
User user = new User();
ONode.ofBean(user).toBean(User.class); //可以作为 bean 转换使用
ONode.ofBean(user).toJson();
ONode.ofJson("{}").toBean(User.class);
ONode.ofJson("[{},{}]").toBean((new ArrayList<User>(){}).getClass());
//快捷方式
String json = ONode.serialize(user);
User user = ONode.deserialize(json, User.class);
5、路径树接口
//case1
ONode o = ONode.ofJson(json);
ONode rst = o.select("$.data.list[*].mobile"); //自动为查询到的节点,生成 path 属性
List<String> rstPaths = rst.pathList(); //获取结果节点的路径列表
for(ONode n1 : rst.getArray()) {
n1.path(); //当前路径
n1.parent(); //父级节点
}
//case2
ONode o = ONode.ofJson(json).usePaths(); //手动为每个子节点,生成 path 属性
ONode rst = o.get("data").get("list").get(2);
rst.path();
rst.parent();
6、高级定制
Json 编解码定制
Options options = Options.of();
//添加编码器
options.addEncoder(Date.class, (ctx, value, target) -> {
target.setValue(DateUtil.format(data, "yyyy-MM-dd"));
});
//添加解码器
options.addDecoder(Date.class, ...);
//添加创建器(接管类实例化)
options.addCreator(...);
//添加特性
options.addFeature(Feature.Write_PrettyFormat);
//移除特性
options.removeFeature(Feature.Write_PrettyFormat);
//设置日期格式附
options.addFeature(Feature.Write_UseDateFormat); //使用日期格式
options.dateFormat("yyyy-MM");
//..
String json = ONode.ofBean(orderModel, options).toJson();
JsonPath 函数与操作符定制
import org.noear.snack4.ONode;
import org.noear.snack4.jsonpath.FunctionLib;
public class FunctionDemo {
public static void main(String[] args) {
//定制 floor 函数
FunctionLib.register("floor", (ctx, argNodes) -> {
ONode arg0 = argNodes.get(0); //节点列表(选择器的结果)
if (ctx.isDescendant()) {
for (ONode n1 : arg0.getArray()) {
if (n1.isNumber()) {
n1.setValue(Math.floor(n1.getDouble()));
}
}
return arg0;
} else {
ONode n1 = arg0.get(0);
if (n1.isNumber()) {
return ctx.newNode(Math.floor(n1.getDouble()));
} else {
return ctx.newNode();
}
}
});
//检验效果(在 IETF 规范里以子项进行过滤,即 1,2) //out: 1.0
System.out.println(ONode.ofJson("{'a':1,'b':2}")
.select("$.a.floor()")
.toJson());
//参考 //out: 2.0
System.out.println(ONode.ofJson("{'a':1,'b':2}")
.select("$[?floor(@) > 1].first()")
.toJson());
}
}
import org.noear.snack4.ONode;
import org.noear.snack4.jsonpath.OperatorLib;
public class OperationDemo {
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;
}
}

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
O2OA v9.5:六大体验升级+技术深研,重塑 OA 开发与使用新范式
兰德网络O2OA(翱途)开发平台以“用户感知为锚点,技术深度为基石”完成重大升级,发布了新版本O2OA v9.5。本次更新不仅带来六大让用户“一眼可见、上手即喜”的体验优化,更在架构底层、流程引擎、安全防护等领域实现技术突破,让开发实施更高效、终端使用更流畅、企业管理更安心。 一、六大核心体验升级:看得见的便捷与高效 1.全新表单组件&样式:颜值与效率双在线 O2OA的表单组件基于浏览器传统UI设计,具有较强的适配能力,能够满足多样化风格样式的表单设计需求。同时,该组件支持与外部UI组件的集成,从而实现更加灵活和丰富的表单设计效果。但这种设计模式在一定程度上提高了开发的技术门槛。由于默认表单样式较为单一,缺乏视觉美感。 O2OA v9.5新增了一套全新的表单UI组件,并结合统一的页面样式规范,旨在通过系统化的方式有效解决上述问题,为用户提供更加美观且一致的表单设计体验。 开发者无需编写复杂CSS,即可使用美观的表单样式快速搭建符合企业的专业化表单;用户端表单视觉层次更清晰,操作引导性更强,填写效率提升25%。 2.表单组件权限:告别“多表单冗余”困境 以往在O2OA平台的表单设...
-
下一篇
pgAdmin 4 v9.9 发布,PostgreSQL 开源图形化管理工具
pgAdmin 是 PostgreSQL 领先的开源图形化管理工具。pgAdmin 4 旨在满足新手和有经验的 Postgres 用户的需求,提供强大的图形界面,简化了数据库对象的创建、维护和使用。 pgAdmin 4 v9.9 现已发布,此版本修复了 10 个错误并新增了一些功能。一些更新亮点包括: Features 为 ALTER FUNCTION 添加 DEPENDS/NO DEPENDS ON EXTENSION 支持。 创建 Range Type时添加了“MULTIRANGE_TYPE_NAME”选项。 创建 External Type 时添加了“SUBSCRIPT”选项。 添加了使用身份文件时跳过密码对话框的选项。 在 PostgreSQL v17+ 的 CREATE/ALTER SUBSCRIPTION 中添加了“failover”和“two_phase”参数支持。 添加对初始化 pgAdmin4 Kubernetes Helm 图表的支持。 Bugs/Housekeeping 修复了查询工具在数据输出中对于空文本数据显示“default”而不是“null”的问题。 修复...
相关文章
文章评论
共有0条评论来说两句吧...