{"id":18854816,"url":"https://github.com/noear/snack3","last_synced_at":"2025-05-16T10:05:36.653Z","repository":{"id":36279025,"uuid":"170295137","full_name":"noear/snack3","owner":"noear","description":"High-performance Jsonpath framework","archived":false,"fork":false,"pushed_at":"2025-05-09T02:31:04.000Z","size":1321,"stargazers_count":139,"open_issues_count":7,"forks_count":25,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-05-16T10:03:57.338Z","etag":null,"topics":["java","json","jsonpath","serialization"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noear.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-02-12T10:03:49.000Z","updated_at":"2025-05-09T02:31:07.000Z","dependencies_parsed_at":"2024-01-15T01:54:58.829Z","dependency_job_id":"6c41e08c-593c-48f1-862b-1570a3dc1d7b","html_url":"https://github.com/noear/snack3","commit_stats":null,"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noear%2Fsnack3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noear%2Fsnack3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noear%2Fsnack3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noear%2Fsnack3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noear","download_url":"https://codeload.github.com/noear/snack3/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254509476,"owners_count":22082891,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","json","jsonpath","serialization"],"created_at":"2024-11-08T03:51:52.751Z","updated_at":"2025-05-16T10:05:36.647Z","avatar_url":"https://github.com/noear.png","language":"Java","readme":"\u003ch1 align=\"center\" style=\"text-align:center;\"\u003e\n  Snack3 for java\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n\t\u003cstrong\u003e一个高性能的 JsonPath 框架\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca target=\"_blank\" href=\"https://search.maven.org/artifact/org.noear/snack3\"\u003e\n        \u003cimg src=\"https://img.shields.io/maven-central/v/org.noear/snack3.svg?label=Maven%20Central\" alt=\"Maven\" /\u003e\n    \u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.apache.org/licenses/LICENSE-2.0.txt\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/:license-Apache2-blue.svg\" alt=\"Apache 2\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-8-green.svg\" alt=\"jdk-8\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-11-green.svg\" alt=\"jdk-11\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-17-green.svg\" alt=\"jdk-17\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk21-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-21-green.svg\" alt=\"jdk-21\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/java/technologies/javase/jdk23-archive-downloads.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-23-green.svg\" alt=\"jdk-23\" /\u003e\n\t\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003ca target=\"_blank\" href='https://gitee.com/noear/snack3/stargazers'\u003e\n        \u003cimg src='https://gitee.com/noear/snack3/badge/star.svg' alt='gitee star'/\u003e\n    \u003c/a\u003e\n    \u003ca target=\"_blank\" href='https://github.com/noear/snack3/stargazers'\u003e\n        \u003cimg src=\"https://img.shields.io/github/stars/noear/snack3.svg?style=flat\u0026logo=github\" alt=\"github star\"/\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr/\u003e\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://jq.qq.com/?_wv=1027\u0026k=kjB5JNiC\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/QQ交流群-22200020-orange\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n\u003chr /\u003e\n\n\n基于jdk8，80kb。支持：序列化反序列化、解析和转换、构建、查找、Json path 查询。\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003eorg.noear\u003c/groupId\u003e\n  \u003cartifactId\u003esnack3\u003c/artifactId\u003e\n  \u003cversion\u003e3.2.133\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nSnack3 借鉴了 `Javascript` 所有变量由 `var` 申明，及 `Xml dom` 一切都是 `Node` 的设计。其下一切数据都以`ONode`表示，`ONode`也即 `One node` 之意，代表任何类型，也可以转换为任何类型。\n* 强调文档树的操控和构建能力\n* 高性能`Json path`查询（兼容性和性能很赞；暂不支持多条件）\n* 顺带支持`序列化、反序列化`\n* 基于 无参构造函数 + 字段 操作实现（因注入而触发动作的风险，不会有）\n\n\n## 性能测试\n\n每个表达式，跑 1_000_000 次的时间：\n\n| Json path 表达式                       | 数据 | fastjson2  | jayway.jsonpath | snack3 |\n|-------------------------------------| --- |------------| --- | -- |\n| `$..a`                              | A | 764ms | 2633ms | 225ms |\n| `$..*`                              | A | (不兼容1)     | 3220ms | 315ms |\n| `$.data.list[1,4]`                  | A | 524ms | 782ms | 133ms |\n| `$.data.list[1:4]`                  | A | 367ms | 941ms | 145ms |\n| `$.data.ary2[1].a`                  | A | 329ms | 663ms | 84ms |\n| `$.data.ary2[*].b.c`                | A | 642ms | 1050ms | 237ms |\n| `$..b[?(@.c == 12)]`                | B | (不兼容2)     | 5636ms | 535ms |\n| `$..c.min()`                        | B | (不兼容2)     | (不兼容2) | 282ms |\n| `$[?(@.c =~ /a+/)]`                 | C | (不兼容2)     | 3591ms | 429ms |\n| `$..ary2[0].a`                      | A | 735ms | 2551ms | 311ms |\n| `$.data.list[?(@ in $..ary2[0].a)]` | A | (不兼容2)     | 5483ms | 674ms |\n\n\n具体可见：\n* [文章_JsonPath_性能测试.md](文章_JsonPath_性能测试.md)\n* [文章_JsonPath_性能测试之2022.md](文章_JsonPath_性能测试之2022.md)\n\n\n\n\n## 放几个示例\n\n```java\n//demo0::字符串化\nString json = ONode.stringify(user); \n\n//美化格式的字符串化\nString json = ONode.load(user, Feature.PrettyFormat).toJson();\n\n\n//demo1::序列化\n// -- 输出带@type\nString json = ONode.serialize(user); \n\n//demo2::反序列化\n// -- json 有已带@type\nUserModel user = ONode.deserialize(json); \n// -- json 可以不带@type (clz 申明了)\nUserModel user = ONode.deserialize(json, UserModel.class); \n// -- json 可以不带@type，泛型方式输出（类型是已知的）\nList\u003cUserModel\u003e list = ONode.deserialize(json, (new ArrayList\u003cUserModel\u003e(){}).getClass());\n// -- 或者\nList\u003cUserModel\u003e list = ONode.deserialize(json, (new TypeRef\u003cList\u003cUserModel\u003e\u003e(){}).getType());\n\n//demo3::转为ONode\nONode o = ONode.loadStr(json); //将json String 转为 ONode\nONode o = ONode.loadObj(user); //将java Object 转为 ONode\n\n//demo3.1::转为ONode，取子节点进行序列化\nONode o = ONode.loadStr(json);\nUserModel user = o.get(\"user\").toObject(UserModel.class);\n\n\n//demo4:构建json数据(极光推送的rest api调用)\nONode data = new ONode();\ndata.set(\"platform\",\"val\");\ndata.getOrNew(\"audience\").getOrNew(\"alias\").addAll(alias_ary);\ndata.getOrNew(\"options\").set(\"apns_production\", false);\nString message = data.toJson();\n//或者....用 build 表达式单行构建\npublic static void push(Collection\u003cString\u003e alias_ary, String text)  {\n    ONode data = new ONode().build((d)-\u003e{\n        d.set(\"platform\", \"all\");\n\n        d.getOrNew(\"audience\").getOrNew(\"alias\").addAll(alias_ary);\n\n        d.getOrNew(\"options\")\n                .set(\"apns_production\",false);\n\n        d.getOrNew(\"notification\").build(n-\u003e{\n            n.getOrNew(\"ios\")\n                    .set(\"alert\",text)\n                    .set(\"badge\",0)\n                    .set(\"sound\",\"happy\");\n        });\n    });\n\n    String message = data.toJson();\n    String author = Base64Util.encode(appKey+\":\"+masterSecret);\n\n    Map\u003cString,String\u003e headers = new HashMap\u003c\u003e();\n    headers.put(\"Content-Type\",\"application/json\");\n    headers.put(\"Authorization\",\"Basic \"+author);\n\n    HttpUtil.postString(apiUrl, message, headers);\n}\n\n//demo5:取值\no.get(\"name\").getString();\no.get(\"num\").getInt();\no.get(\"list\").get(0).get(\"lev\").getInt();\n\n//demo5.1::取值并转换\nUserModel user = o.get(\"user\").toObject(UserModel.class); //取user节点，并转为UserModel\n\n//demo5.2::取值或新建并填充\no.getOrNew(\"list2\").fill(\"[1,2,3,4,5,5,6]\");\n\n\n//demo6::json path //不确定返回数量的，者会返回array类型\n//找到所有的187开头的手机号，改为186，最后输出修改后的json\no.select(\"$..mobile[?(@ =~ /^187/)]\").forEach(n-\u003en.val(\"186\")).toJson();\n//找到data.list[1]下的的mobile字段，并转为long\no.select(\"$.data.list[1].mobile\").getLong();\n//如果没有则创建，并赋值\no.selectOrNew(\"$.test.list[2]\").val(12);        \n\n//查找所有手机号，并转为List\u003cString\u003e \nList\u003cString\u003e list = o.select(\"$..mobile\").toObject(List.class);\n//查询data.list下的所有mobile，并转为List\u003cString\u003e\nList\u003cString\u003e list = o.select(\"$.data.list[*].mobile\").toObject(List.class);\n//找到187手机号的用户，并输出List\u003cUserModel\u003e\nList\u003cUserModel\u003e list = o.select(\"$.data.list[?(@.mobile =~ /^187/)]\")\n                        .toObjectList(UserModel.class);\n//或\nList\u003cUserModel\u003e list = o.select(\"$.data.list[?(@.mobile =~ /^187/)]\")\n                        .toObjectList(UserModel.class);\n\n\n//demo7:遍历\n//如果是个Object\no.forEach((k,v)-\u003e{\n  //...\n});\n//如果是个Array\no.forEach((v)-\u003e{\n  //...\n});\n```\n\n#### 路径树接口\n\n```java\nONode o = ONode.loadStr(json).usePaths(); //会为每个子节点，生成 path 属性\n\nONode rst = o.select(\"$.data.list[*].mobile\");\nList\u003cString\u003e rstPaths = rst.pathList(); //获取结果节点的路径列表\nfor(ONode n1 : rst.ary()) {\n   n1.path(); //当前路径\n   n1.parent(); //父级节点\n}\n\nONode rst = o.get(\"data\").get(\"list\").get(2);\nrst.path();\nrst.parent();\n```\n\n\n#### 高级定制\n\n```java\nOptions options = Options.def();\n//添加编码器\noptions.addEncoder(Date.class, (data, node) -\u003e {\n    node.val().setString(DateUtil.format(data, \"yyyy-MM-dd\"));\n});\n//添加解码器\noptions.addDecoder(Date.class, ...);\n\n//添加特性\noptions.add(Feature.PrettyFormat);\n\n//移除特性\noptions.remove(Feature.PrettyFormat);\n\n//设置日期格式附\noptions.add(Feature.WriteDateUseFormat); //使用日期格式\noptions.setDateFormat(\"yyyy-MM\");\n\n//..\n\nString json = ONode.loadObj(orderModel, options).toJson();\n```\n\n\n\n## 关于序列化的特点\n#### 对象（可以带type）\n```json\n{\"a\":1,\"b\":\"2\"}\n//或\n{\"@type\":\"...\",\"a\":1,\"b\":\"2\"}\n```\n#### 数组\n```json\n[1,2,3]\n//或\n[{\"@type\":\"...\",\"a\":1,\"b\":\"2\"},{\"@type\":\"...\",\"a\":2,\"b\":\"10\"}]\n```\n\n#### 注解定制\n\n```java\npublic enum BookType {\n    NOVEL(2,\"小说\"),\n    CLASSICS(3,\"名著\"),\n    ;\n\n    @ONodeAttr public final int code; //使用 code 做为序列化的字段\n    public final String des;\n    BookType(int code, String des){this.code=code; this.des=des;}\n}\n\npublic class Book {\n    String name;\n    BookType type;\n    @ONodeAttr(serialize=false) String author; //不序列化\n    @ONodeAttr(format=\"yyyy-MM-dd\") Date releaseTime; //格式化时间输出\n}\n```\n\n## 关于Json path的支持\n* 字符串使用单引号，例：\\['name']\n* 过滤操作用空隔号隔开，例：\\[?(@.type == 1)]\n* 提醒：当有(注1)操作符时，会外面包了一层 array，后续操作符将作用于它的 item。\n\n| 支持操作                       | \t说明                                |\n|----------------------------|------------------------------------|\n| `$`\t                       | 表示根元素                              |\n| `@`\t                       | 当前节点（做为过滤表达式的谓词使用）                 |\n| `*`\t                       | (注1)通用匹配符，可以表示一个名字或数字。             |\n| `..`\t                      | (注1)深层扫描。 可以理解为递归搜索。                   |\n| `.\u003cname\u003e`\t                 | 表示一个子节点                            |\n| `['\u003cname\u003e' (, '\u003cname\u003e')]`  | 表示一个或多个子节点                         |\n| `[\u003cnumber\u003e (, \u003cnumber\u003e)]`\t | 表示一个或多个数组下标（负号为倒数）                 |\n| `[start:end]`\t             | 数组片段，区间为\\[start,end),不包含end（负号为倒数） |\n| `[?(\u003cexpression\u003e)]`\t       | (注1)过滤表达式。 表达式结果必须是一个布尔值。              |\n| `[?(@.\u003cname\u003e)]`\t           | (注1)过滤表达式。 表示有一个子节点。                   |\n\n\n| 支持过滤操作符(`操作符两边要加空隔`) |\t说明 |\n| --- | --- |\n| `==`\t| left等于right（注意1不等于'1'） |\n| `!=`\t| 不等于 |\n| `\u003c`\t| 小于 |\n| `\u003c=`\t| 小于等于 |\n| `\u003e`\t| 大于 |\n| `\u003e=`\t| 大于等于 |\n| `=~`\t| 匹配正则表达式[?(@.name =~ /foo.*?/i)] |\n| `in`\t| 左边存在于右边 [?(@.size in ['S', 'M'])] |\n| `nin`\t| 左边不存在于右边 |\n\n| 支持尾部函数（对最后结果计算） | \t说明                 |\n|-----------------|---------------------|\n| `min()`\t        | 计算数字数组的最小值          |\n| `max()`\t        | 计算数字数组的最大值          |\n| `avg()`\t        | 计算数字数组的平均值          |\n| `sum()`\t        | 计算数字数组的汇总值（新加的）     |\n| \t               |                     |\n| `size()`\t       | 大小（当对象或数组时有效）       |\n| `length()`\t     | 当字符串时，为字符串长度；否则为大小  |\n| `keys()`\t       | 当对象时返回所有key；否则为null |\n| \t               |                     |\n| `first()`\t      | 获取数组的第一项            |\n| `last()`\t       | 获取数组的最后项            |\n\n\n例：`n.select(\"$.store.book[0].title\")` 或 `n.select(\"$['store']['book'][0]['title']\")`\n\n例：`n.select(\"$..book.price.min()\") //找到最低的价格`\n\n\n\n# Snack3 接口字典\n\n* [文章_Snack3_接口字典.md](文章_Snack3_接口字典.md)\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoear%2Fsnack3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoear%2Fsnack3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoear%2Fsnack3/lists"}