Skip to content

Repository files navigation

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 DeepWikiMavenApache 2jdk-8jdk-11jdk-17jdk-21jdk-25
gitee stargithub star


Language: English | 中文

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) 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)
dependenciesdescription
org.noear:snack4Provides json dom building and codec support
org.noear:snack4-jsonpathProvides json path query support
org.noear:snack4-jsonschemaProvides json schema validation support

JSONPath syntax reference ( IETF JSONPath (RFC 9535) )

Syntax ElementDescription
$root node identifier
@current node identifier (valid only within filter selectors)
[<selectors>]child segment: selects zero or more children of a node
.nameshorthand for ['name']
.*shorthand for [*]
..[<selectors>]descendant segment: selects zero or more descendants of a node
..nameshorthand for ..['name']
..*shorthand for ..[*]
'name'name selector: selects a named child of an object
*wildcard selector: selects all children of a node
3index selector: selects an indexed child of an array (from 0)
0:100:5array slice selector: start:end:step for arrays
?<logical-expr>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:

SyntaxDescriptionPrecedence
(...)Grouping5
name(...)Function Expressions5
!Logical NOT4
==,!=,<,<=,>,>=Relations3
&&Logical AND2
||Logical OR1

IETF JSONPath (RFC 9535) Standard definition operators (supported)

OperatorDescriptionExamples
==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)

OperatorDescriptionExamples
=~left matches regular expression[?(@.s =~ /foo.*?/i)]
inleft exists in right[?(@.s in ['S', 'M'])]
ninleft does not exists in right
subsetofleft is a subset of right[?(@.s subsetof ['S', 'M', 'L'])]
anyofleft has an intersection with right[?(@.s anyof ['M', 'L'])]
noneofleft has no intersection with right[?(@.s noneof ['M', 'L'])]
sizesize of left (array or string) should match right$[?(@.s size @.expected_size)]
emptyleft (array or string) should be empty$[?(@.s empty false)]

IETF JSONPath (RFC 9535) Standard definition functions (supported)

FunctionDescriptionParameter typesResult types
length(x)The length of a string, array, or objectValueNumerical value
count(x)Size of the node listNode listNumerical value
match(x,y)The regular expression matches exactlyValue,ValueLogical value
search(x,y)Regular expression substring matchingValue,ValueLogical value
value(x)The value of a single node in a node listNode listValue

jayway.jsonpath Functions (supported)

FunctionDescriptionOutput type
min()Provides the min value of an array of numbersDouble
max()Provides the max value of an array of numbersDouble
avg()Provides the average value of an array of numbersDouble
stddev()Provides the standard deviation value of an array of numbersDouble
length()Provides the length of an arrayInteger
sum()Provides the sum value of an array of numbersDouble
keys()Provides the property keys (An alternative for terminal tilde ~)Set<E>
concat(X)Provides a concatinated version of the path output with a new itemlike input
append(X)add an item to the json path output arraylike input
first()Provides the first item of an arrayDepends on the array
last()Provides the last item of an arrayDepends on the array
index(X)Provides the item of an array of index: X, if the X is negative, take from backwardsDepends on the array

snack-jsonpath Increment operator (supported)

OperatorDescriptionExamples
startsWithleft (string) start matches a right[?(@.s startsWith 'a')]
endsWithleft (string) end matches the right[?(@.s endsWith 'b')]
containsleft (array or string) contains matches the right[?(@.s contains 'c')]

JSONPath syntax examples

Example JSON Value

{ "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

JSONPathIntended Result
$.store.book[*].authorthe authors of all books in the store
$..authoall authors
$.store.*all things in the store, which are some books and a red bicycle
$.store..pricethe prices of everything in the store
$..book[2]the third book
$..book[2].authorthe third book's author
$..book[2].publisherempty 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

ONodeoNode = newONode();
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

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

JsonSchemaschema = JsonSchema.ofJson("{type:'object',properties:{userId:{type:'string'}}}"); //加载架构定义schema.validate(ONode.ofJson("{userId:'1'}")); //校验格式

Supports serialization and deserialization

Useruser = newUser();
ONode.ofBean(user).toBean(User.class); //可以作为 bean 转换使用ONode.ofBean(user).toJson();
ONode.ofJson("{}").toBean(User.class);
ONode.ofJson("[{},{}]").toBean((newArrayList<User>(){}).getClass()); //泛型ONode.ofJson("[{},{}]").toBean(newTypeRef<List<User>>(){}); //泛型//快捷方式Stringjson = ONode.serialize(user);
Useruser = ONode.deserialize(json, User.class);

Path tree interface

//case1ONodeo = ONode.ofJson(json); ONoderst = o.select("$.data.list[*].mobile"); //自动为查询到的节点,生成 path 属性List<String> rstPaths = rst.pathList(); //获取结果节点的路径列表for(ONoden1 : rst.getArray()) {
n1.path(); //当前路径n1.parent(); //父级节点
}
//case2ONodeo = ONode.ofJson(json).usePaths(); //手动为每个子节点,生成 path 属性ONoderst = o.get("data").get("list").get(2);
rst.path();
rst.parent();

Advanced customization

Json codec customization

Optionsoptions = 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");
//..Stringjson = ONode.ofBean(orderModel, options).toJson();

JsonPath Function and operator customization

importorg.noear.snack4.ONode;
importorg.noear.snack4.jsonpath.FunctionLib;
publicclassFunctionDemo {
publicstaticvoidmain(String[] args) {
//定制 floor 函数FunctionLib.register("floor", (ctx, argNodes) -> {
ONodearg0 = argNodes.get(0); //节点列表(选择器的结果)if (ctx.isDescendant()) {
for (ONoden1 : arg0.getArray()) {
if (n1.isNumber()) {
n1.setValue(Math.floor(n1.getDouble()));
}
}
returnarg0;
} else {
ONoden1 = arg0.get(0);
if (n1.isNumber()) {
returnctx.newNode(Math.floor(n1.getDouble()));
} else {
returnctx.newNode();
}
}
});
//检验效果(在 IETF 规范里以子项进行过滤,即 1,2) //out: 1.0System.out.println(ONode.ofJson("{'a':1,'b':2}")
.select("$.a.floor()")
.toJson());
//参考 //out: 2.0System.out.println(ONode.ofJson("{'a':1,'b':2}")
.select("$[?floor(@) > 1].first()")
.toJson());
}
}
importorg.noear.snack4.ONode;
importorg.noear.snack4.jsonpath.OperatorLib;
publicclassOperationDemo {
publicstaticvoidmain(String[] args) {
//定制操作符OperatorLib.register("startsWith", (ctx, node, term) -> {
ONodeleftNode = term.getLeftNode(ctx, node);
if (leftNode.isString()) {
ONoderightNode = term.getRightNode(ctx, node);
if (rightNode.isNull()) {
returnfalse;
}
returnleftNode.getString().startsWith(rightNode.getString());
}
returnfalse;
});
//检验效果assertONode.ofJson("{'list':['a','b','c']}")
.select("$.list[?@ startsWith 'a']")
.size() == 1;
}
}

Special thanks to JetBrains for supporting open-source projects:

JetBrains

About

Sanck4: Next-generation high-performance Jsonpath framework. Compatible with 'jayway.jsonpath' and IETF JSONPath (RFC 9535) standard specifications (open customization support).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages