[Open Source] A lightweight, high-performance JsonPath framework, a powerful tool for parsing JSON in Java

Open SourceClassification Systems
2026-06-28 08:36 5 次浏览 • 0 次下载 0KB
[Open Source] A lightweight, high-performance JsonPath framework, a powerful tool for parsing JSON in Java

1. Project Overview

Snack3 for java

A High-Performance JsonPath Framework

Based on JDK 8, with a size of 80 KB. Supports serialization and deserialization, parsing and conversion, construction, searching, and JsonPath queries.

2. Open Source License

Licensed under Apache-2.0

3. Screenshots

4. Feature Overview

Snack3 draws inspiration from JavaScript, where all variables are declared using var, and XML DOM, where everything is a Node. In Snack3, all data is represented by ONode, which stands for One Node. ONode represents any data type and can also be converted into any type.

  • Emphasizes document tree manipulation and construction capabilities
  • High-performance JsonPath queries (excellent compatibility and performance)
  • Also supports serialization and deserialization
  • Implemented using no-argument constructors + fields (eliminating the risk of actions being triggered by injection)

5. Tech Stack


<dependency>
  <groupId>org.noear</groupId>
  <artifactId>snack3</artifactId>
  <version>3.2.74</version>
</dependency>

Performance Testing

Execution time for each expression, running 1,000,000 times:

JsonPath Expression

Data

fastjson2

json-path

snack3

$..a

A

764ms

2633ms

225ms

$..*

A

(Not compatible 1)

3220ms

315ms

$.data.list[1,4]

A

524ms

782ms

133ms

$.data.list[1:4]

A

367ms

941ms

145ms

$.data.ary2[1].a

A

329ms

663ms

84ms

$.data.ary2[*].b.c

A

642ms

1050ms

237ms

$..b[?(@.c == 12)]

B

(Not compatible 2)

5636ms

535ms

$..c.min()

B

(Not compatible 2)

(Not compatible 2)

282ms

$[?(@.c =~ /a+/)]

C

(Not compatible 2)

3591ms

429ms

$..ary2[0].a

A

735ms

2551ms

311ms

$.data.list[?(@ in $..ary2[0].a)]

A

(Not compatible 2)

5483ms

674ms

For details, see:

  • Article_JsonPath_Performance_Test.md
  • Article_JsonPath_Performance_Test_2022.md

Here are a few examples


//demo0::String conversion
String json = ONode.stringify(user); 

//Pretty-formatted string conversion
String json = ONode.load(user, Feature.PrettyFormat).toJson();


//demo1::Serialization
// -- Output with @type
String json = ONode.serialize(user); 

//demo2::Deserialization
// -- JSON already contains @type
UserModel user = ONode.deserialize(json); 
// -- JSON can omit @type (clz is declared)
UserModel user = ONode.deserialize(json, UserModel.class); 
// -- JSON can omit @type, using generic type output (the type is known)
List<UserModel> list = ONode.deserialize(json, (new ArrayList<UserModel>(){}).getClass()); 

//demo3::Convert to ONode
ONode o = ONode.loadStr(json); //Convert JSON String to ONode
ONode o = ONode.loadObj(user); //Convert Java Object to ONode

//demo3.1::Convert to ONode and serialize a child node
ONode o = ONode.loadStr(json);
UserModel user = o.get("user").toObject(UserModel.class);


//demo4:Build JSON data (JPush REST API call)
public static void push(Collection<String> alias_ary, String text)  {
    ONode data = new ONode().build((d)->{
        d.getOrNew("platform").val("all");

        d.getOrNew("audience").getOrNew("alias").addAll(alias_ary);

        d.getOrNew("options")
                .set("apns_production",false);

        d.getOrNew("notification").build(n->{
            n.getOrNew("ios")
                    .set("alert",text)
                    .set("badge",0)
                    .set("sound","happy");
        });
    });

    String message = data.toJson();
    String author = Base64Util.encode(appKey+":"+masterSecret);

    Map<String,String> headers = new HashMap<>();
    headers.put("Content-Type","application/json");
    headers.put("Authorization","Basic "+author);

    HttpUtil.postString(apiUrl, message, headers);
}

//demo5:Get values
o.get("name").getString();
o.get("num").getInt();
o.get("list").get(0).get("lev").getInt();

//demo5.1::Get a value and convert it
UserModel user = o.get("user").toObject(UserModel.class); //Get the user node and convert it to UserModel

//demo5.2::Get or create a node and populate it
o.getOrNew("list2").fill("[1,2,3,4,5,5,6]");


//demo6::JSONPath //When the number of returned results is uncertain, an array type is returned
//Find all mobile numbers starting with 187, change them to 186, and output the modified JSON
 o.select("$..mobile[?(@ =~ /^187/)]").forEach(n->n.val("186")).toJson();
//Find the mobile field under data.list[1] and convert it to long
o.select("$.data.list[1].mobile").getLong();

//Find all mobile numbers and convert them to List<String> 
List<String> list = o.select("$..mobile").toObject(List.class);
//Find all mobile numbers under data.list and convert them to List<String>
List<String> list = o.select("$.data.list[*].mobile").toObject(List.class);
//Find users with mobile numbers starting with 187 and output List<UserModel>
List<UserModel> list = o.select("$.data.list[?(@.mobile =~ /^187/)]")
                        .toObjectList(UserModel.class);
//Or
List<UserModel> list = o.select("$.data.list[?(@.mobile =~ /^187/)]")
                        .toObjectList(UserModel.class);


//demo7:Iteration
//If it is an Object
o.forEach((k,v)->{
  //...
});
//If it is an Array
o.forEach((v)->{
  //...
});


//demo8:Custom encoding (addDecoder adds a parser for deserialization)
Options options = Options.def();
options.addEncoder(Date.class, (data, node) -> {
    node.val().setString(DateUtil.format(data, "yyyy-MM-dd"));
});

String json = ONode.loadObj(orderModel, options).toJson();

Serialization Features

Object (can include type)


{"a":1,"b":"2"}
//or
{"@type":"...","a":1,"b":"2"}

Array


[1,2,3]
//or
[{"@type":"...","a":1,"b":"2"},{"@type":"...","a":2,"b":"10"}]

Annotation Customization


public enum BookType {
    NOVEL(2,"Novel"),
    CLASSICS(3,"Classics"),
    ;

    @ONodeAttr public final int code; //Use code as the serialized field
    public final String des;
    BookType(int code, String des){this.code=code; this.des=des;}
}

public class Book {
    String name;
    BookType type;
    @ONodeAttr(serialize=false) String author; //Do not serialize
    @ONodeAttr(format="yyyy-MM-dd") Date releaseTime; //Format date output
}

Custom Encoding and Decoding


Options options = Options.def();
options.addEncoder(Date.class, (data, node) -> {
    node.val().setString(DateUtil.format(data, "yyyy-MM-dd"));
});
options.addDecoder(Date.class, ...);

String json = ONode.loadObj(orderModel, options).toJson();

JSONPath Support

  • Use single quotes for strings, e.g.: ['name']
  • Separate filter operations with spaces, e.g.: [?(@.type == 1)]

Supported Operations

Description

$

Represents the root element

@

Current node (used as a predicate in filter expressions)

*

Universal wildcard, representing a name or number.

..

Deep scan. Can be understood as recursive search.

.<name>

Represents a child node

['<name>' (, '<name>')]

Represents one or more child nodes

[<number> (, <number>)]

Represents one or more array indexes (negative numbers count backward)

[start:end]

Array slice, range is [start,end), excluding end (negative numbers count backward)

[?(<expression>)]

Filter expression. The expression result must be a Boolean value.

[?(@.<name>)]

Filter expression. Indicates that a child node exists.

Supported filter operators (spaces are required on both sides of the operator)

Description

==

left equals right (note that 1 is not equal to '1')

!=

Not equal to

<

Less than

<=

Less than or equal to

>

Greater than

>=

Greater than or equal to

=~

Matches a regular expression [?(@.name =~ /foo.*?/i)]

in

Left operand exists in right operand [?(@.size in ['S', 'M'])]

nin

Left operand does not exist in right operand

Supported terminal functions

Description

min()

Calculate the minimum value of a numeric array

max()

Calculate the maximum value of a numeric array

avg()

Calculate the average value of a numeric array

sum()

Calculate the sum of a numeric array (newly added)

Example: n.select("$.store.book[0].title") or n.select("$['store']['book'][0]['title']")

Example: n.select("$..book.price.min()") //Find the lowest price

Snack3 API Dictionary

  • Article_Snack3_API_Dictionary.md



#1
资源直链https://gitee.com/noear/snack3
去下载
星河码客

发现和分享优质开源项目,传承互联网精神,为开发者服务。

关于本站

联系我们

微信/广告合作:ITcloub (备注星河码客)

免责声明:本站资源版权归原作者所有,仅供学习与交流。如有侵权,请通过「关于我们 - 侵权投诉」联系删除。

Powered by Nuxt · Deployed on Cloudflare