# snackjson
**Repository Path**: zzchinahebei/snackjson
## Basic Information
- **Project Name**: snackjson
- **Description**: 新一代高性能 Jsonpath 框架。同时兼容 `jayway.jsonpath` 和 IETF JSONPath (RFC 9535) 标准规范(支持开放式定制)。
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: https://solon.noear.org/article/snack
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 80
- **Created**: 2026-03-18
- **Last Updated**: 2026-03-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
SnackJson
A Json framework with support for JsonDom, JsonPath, JsonSchema (for Java)
Compatible ` jayway. Jsonpath ` and IETF jsonpath (RFC 9535) standard. Compatible with JsonSchema Draft07 standard. Support for open customization.
https://solon.noear.org/article/snack
##### Language: English | [中文](README_CN.md)
SnackJson draws on the design of `Javascript` where all variables are declared with `var` and `Xml dom` where everything is `Node`. Everything underneath it is represented by an `ONode`, which stands for `One node` and can be converted to any type.
* Emphasize the ability to build and manipulate the `Json dom`
* High performance `Json path` queries (much faster than `jayway.jsonpath`), Compatible with `jayway.jsonpath` and [IETF JSONPath (RFC 9535)](https://www.rfc-editor.org/rfc/rfc9535.html) standards (Switch with `optoins`). Deliver the next generation of JsonPath experiences.
* Provides `Json schema` construction and validation, compatible with JsonSchema draw-07, draw-2019 standards
* Support some 'json5' features (keyless fields, comments, etc...)
* Prefer no-argument constructors + field codec (reduces the risk of triggering actions by injection)
| dependencies | description |
|-------------------------------------|--------------------------------------------|
| `org.noear:snack4` | Provides `json dom` building and codec support |
| `org.noear:snack4-jsonpath` | Provides `json path` query support |
| `org.noear:snack4-jsonschema` | Provides `json schema` validation support |
### JSONPath syntax reference ( [IETF JSONPath (RFC 9535)](https://www.rfc-editor.org/rfc/rfc9535.html) )
| Syntax Element | Description |
|-------------------|----------------------------------------------------------------------------|
| `$` | root node identifier |
| `@` | current node identifier (valid only within filter selectors) |
| `[]` | child segment: selects zero or more children of a node |
| `.name` | shorthand for `['name']` |
| `.*` | shorthand for `[*]` |
| `..[]` | descendant segment: selects zero or more descendants of a node |
| `..name` | shorthand for `..['name']` |
| `..*` | shorthand for `..[*]` |
| `'name'` | name selector: selects a named child of an object |
| `*` | wildcard selector: selects all children of a node |
| `3` | index selector: selects an indexed child of an array (from 0) |
| `0:100:5` | array slice selector: `start:end:step` for arrays |
| `?` | filter selector: selects particular children using a logical expression |
| `fun(@.foo)` | filter function: invokes a function in a filter expression (IETF standard) |
| `.fun()` | aggregate function: Used as a fragment (jayway style) |
Filter selector syntax reference:
| Syntax | Description | Precedence |
|------------------------------|------------------------|------------|
| `(...)` | Grouping | 5 |
| `name(...)` | Function Expressions | 5 |
| `!` | Logical NOT | 4 |
| `==`,`!=`,`<`,`<=`,`>`,`>=` | Relations | 3 |
| `&&` | Logical AND | 2 |
| `\|\|` | Logical OR | 1 |
IETF JSONPath (RFC 9535) Standard definition operators (supported)
| Operator | Description | Examples |
|-----------|----------------------------------------------------------|------------------|
| `==` | left is equal to right (note that 1 is not equal to '1') | `$[?(@.a == 1)]` |
| `!=` | left is not equal to right | `$[?(@.a != 1)]` |
| `<` | left is less than right | `$[?(@.a < 1)]` |
| `<=` | left is less or equal to right | `$[?(@.a <= 1)]` |
| `>` | left is greater than right | `$[?(@.a > 1)]` |
| `>=` | left is greater than or equal to right | `$[?(@.a >= 1)]` |
jayway.jsonpath Increment operator (supported)
| Operator | Description | Examples |
|------------|---------------------------------------------------|-------------------------------------|
| `=~` | left matches regular expression | `[?(@.s =~ /foo.*?/i)]` |
| `in` | left exists in right | `[?(@.s in ['S', 'M'])]` |
| `nin` | left does not exists in right | |
| `subsetof` | left is a subset of right | `[?(@.s subsetof ['S', 'M', 'L'])]` |
| `anyof` | left has an intersection with right | `[?(@.s anyof ['M', 'L'])]` |
| `noneof` | left has no intersection with right | `[?(@.s noneof ['M', 'L'])]` |
| `size` | size of left (array or string) should match right | `$[?(@.s size @.expected_size)]` |
| `empty` | left (array or string) should be empty | `$[?(@.s empty false)]` |
IETF JSONPath (RFC 9535) Standard definition functions (supported)
| Function | Description | Parameter types | Result types |
|---------------|-------------------------------------------|-----------------|-------------------|
| `length(x)` | The length of a string, array, or object | Value | Numerical value |
| `count(x)` | Size of the node list | Node list | Numerical value |
| `match(x,y)` | The regular expression matches exactly | Value,Value | Logical value |
| `search(x,y)` | Regular expression substring matching | Value,Value | Logical value |
| `value(x)` | The value of a single node in a node list | Node list | Value |
jayway.jsonpath Functions (supported)
| Function | Description | Output type |
|:------------|:-------------------------------------------------------------------------------------|:---------------------|
| `min()` | Provides the min value of an array of numbers | Double |
| `max()` | Provides the max value of an array of numbers | Double |
| `avg()` | Provides the average value of an array of numbers | Double |
| `stddev()` | Provides the standard deviation value of an array of numbers | Double |
| `length()` | Provides the length of an array | Integer |
| `sum()` | Provides the sum value of an array of numbers | Double |
| `keys()` | Provides the property keys (An alternative for terminal tilde `~`) | `Set` |
| `concat(X)` | Provides a concatinated version of the path output with a new item | like input |
| `append(X)` | add an item to the json path output array | like input |
| `first()` | Provides the first item of an array | Depends on the array |
| `last()` | Provides the last item of an array | Depends on the array |
| `index(X)` | Provides the item of an array of index: X, if the X is negative, take from backwards | Depends on the array |
snack-jsonpath Increment operator (supported)
| Operator | Description | Examples |
|------------------|---------------------------------------------------|---------------------------|
| `startsWith` | left (string) start matches a right | `[?(@.s startsWith 'a')]` |
| `endsWith` | left (string) end matches the right | `[?(@.s endsWith 'b')]` |
| `contains` | left (array or string) contains matches the right | `[?(@.s contains 'c')]` |
### JSONPath syntax examples
Example JSON Value
```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
}
}
}
```
Example JSONPath Expressions and Their Intended Results When Applied to the Example JSON Value
| JSONPath | Intended Result |
| -------- | -------- |
| `$.store.book[*].author` | the authors of all books in the store |
| `$..autho` | all authors |
| `$.store.*` | all things in the store, which are some books and a red bicycle |
| `$.store..price` | the prices of everything in the store |
| `$..book[2]` | the third book |
| `$..book[2].author` | the third book's author |
| `$..book[2].publisher` | empty result: the third book does not have a "publisher" member |
| `$..book[-1]` | the last book in order |
| `$..book[0,1]`
`$..book[:2]` | the first two books |
| `$..book[?@.isbn]` | all books with an ISBN number |
| `$..book[?@.price<10]` | all books cheaper than 10 |
| `$..*` | all member values and array elements contained in the input value |
### Let's look at some application examples
Support `dom` manipulation
```java
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]");
```
Supports `json path` query, build, and delete
```java
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]");
```
Supports `json schema` validation
```java
JsonSchema schema = JsonSchema.ofJson("{type:'object',properties:{userId:{type:'string'}}}"); //加载架构定义
schema.validate(ONode.ofJson("{userId:'1'}")); //校验格式
```
Supports serialization and deserialization
```java
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(){}).getClass()); //泛型
ONode.ofJson("[{},{}]").toBean(new TypeRef>(){}); //泛型
//快捷方式
String json = ONode.serialize(user);
User user = ONode.deserialize(json, User.class);
```
### Path tree interface
```java
//case1
ONode o = ONode.ofJson(json);
ONode rst = o.select("$.data.list[*].mobile"); //自动为查询到的节点,生成 path 属性
List 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();
```
### Advanced customization
Json codec customization
```java
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 Function and operator customization
```java
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());
}
}
```
```java
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;
}
}
```
### Special thanks to JetBrains for supporting open-source projects: