# 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

Ask DeepWiki Maven Apache 2 jdk-8 jdk-11 jdk-17 jdk-21 jdk-25
gitee star github star


##### 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: JetBrains