🏆 Tencent Top 6 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
-
----
-
-#### A better online document is available at https://apijsondocs.readthedocs.io/
-
-* ### [1.About](#1)
-* ### [2.Backend usage](#2)
-* ### [3.Frontend usage](#3)
-* ### [4.Contributing](#4)
-* ### [5.Releases](#5)
-* ### [6.Author](#6)
-* ### [7.Donating](#7)
-
-
-
-##
1. About
-
-APIJSON is a JSON based internet communication protocol and an ORM library
-that largely simplifies the process of back-end API development.
-It also allows users to get data more quickly with self-defined form and fewer endpoints requests.
-
-### Features:
-#### For getting data:
-You can get any data by defining the specific information you want and send it to the server.
-You can get different types of data by making just one request to the server.
-It's very convenient and flexible, and dosen't require different API endpoints with multiple requests.
-It provides CRUD(read and write), Fuzzy Search, Remote Function Calls, etc.
-You can also save duplicate data, see request history, etc.
-
-#### For API design:
-APIJSON largely reduces API developers' workload by reducing most api design and documentation work.
-With APIJSON, client developers will no longer be suffered from possible errors in documents,
-and it saves communication between server developers and client developers about APIs or documentations.
-Server developers no longer need to worry about compatibility of APIs and documents with legacy apps.
-
-
-#### Song Firework-Katy Parry(Modified for APIJSON)
-Do you ever feel like a backend slave
-Repeating CRUD, wanting to make a change?
-Do you ever feel, APIs' so paper thin
-Like a house of cards, one blow from cavin' in?
-Do you ever feel they always complain?
-Urging doc and feedback bugs, even ask your refactoring
-Do you know that there's still a chance for you?
-'Cause there's a powerful tool
-You just gotta depend and configure
-And let it init
-Just start APIs
-They are so easy to try
-'Cause baby, you're a firework
-Come on, show 'em what you're worth
-Make 'em go, "Oh, oh, oh"
-As you give 'em an A-T-M
-Baby, you're a firework
-Come on, let them serve themselves
-Make 'em go, "Oh, oh, oh"
-You're gonna leave 'em all in awe, awe, awe.
-
-
-
-**Tired with endless arguments about HTTP API dev or use?**
-**Use APIJSON-the ORM for providing infinity codeless CRUD APIs that fit almost all your needs.**
-**Unfold the Power(In Your Soul) with ⭐Star & Clone.**
-
-### APIJSON Show
-#### Postman test APIJSON
-
-
-
-#### APIAuto test APIJSON
-Note: The UI is APIAuto, the URL+JSON is APIJSON
-
-
2.Backend usage
-You can skip this step and use 'apijson.cn:8080'.
-See https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Java-Server/README-English.md
-
-
-
-##
3. Frontend usage
-You can skip this step and use [APIAuto](https://github.com/TommyLemon/APIAuto) or download App.
-See [Android](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-Android/README-English.md), [iOS](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-iOS/README-English.md) or [JavaScript](https://github.com/APIJSON/APIJSON-Demo/blob/master/APIJSON-JavaScript/README-English.md)
-
-### Download App
-
-Simple demo App for testing APIJSON
-[APIJSONTest.apk](http://files.cnblogs.com/files/tommylemon/APIJSONTest.apk)
-
-Complex production App like Twitter tweets
-[APIJSONApp.apk](http://files.cnblogs.com/files/tommylemon/APIJSONApp.apk)
-
-
-
-##
4. Contributing
-
-We are always looking for more developers to help implementing new features, fix bugs, etc.
-Please have a look at the [open issues](https://github.com/Tencent/APIJSON/issues) before opening a new one.
-
-Fork the project and send a pull request.
-
-Please also ⭐Star the project!
-
-
-##
6. Author
-
-Check out the author's [github account](https://github.com/TommyLemon) to see more related projects.
-
-
-If you have any questions or suggestions, you can [create an issue](https://github.com/Tencent/APIJSON/issues) or [send me an e-mail](mailto:tommylemon@qq.com).
-
-
-
-
-### Users of APIJSON:
-
-https://github.com/Tencent/APIJSON/issues/187
-
-
-
-Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) specialist, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
-https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
-https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-
-
-
-Thanks to all contributers of APIJSON!
-
-
-
-### Statistics
-Hundreds of employees from big famous companies(Tencent, Google, Apple, Microsoft, Amazon, Huawei, Alibaba, Paypal, Meituan, Bytedance(TikTok), IBM, Baidu, JD, NetEase, Kuaishou, Shopee, etc.) starred,
-a lot of employees from big famous companies(Tencent, Huawei, Microsoft, Zoom, etc.) created PR/Issue, thank you all~
-[](https://starchart.cc/Tencent/APIJSON)
-
-
-
-
-
-
diff --git a/README.md b/README.md
index 72e15ba6..ccafb985 100644
--- a/README.md
+++ b/README.md
@@ -6,14 +6,15 @@ This source code is licensed under the Apache License Version 2.0
APIJSON
-
🏆 Tencent Top 6 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
@@ -21,36 +22,22 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -60,15 +47,12 @@ This source code is licensed under the Apache License Version 2.0
-
-
-
@@ -82,48 +66,77 @@ This source code is licensed under the Apache License Version 2.0
---
-导航目录: 项目简介 [上手使用](#%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B) [社区生态](#%E6%8A%80%E6%9C%AF%E4%BA%A4%E6%B5%81) 完整详细的导航目录 [点这里查看](/Navigation.md)
+#### A better online document is available at https://apijsondocs.readthedocs.io/
+* ### [1.About](#1)
+* ### [2.Backend usage](#2)
+* ### [3.Frontend usage](#3)
+* ### [4.Contributing](#4)
+* ### [5.Releases](#5)
+* ### [6.Author](#6)
+* ### [7.Donating](#7)
-APIJSON 是一种专为 API 而生的 JSON 网络传输协议 以及 基于这套协议实现的 ORM 库。
-为各种增删改查提供了完全自动化的万能通用接口,零代码实时满足千变万化的各种新增和变更需求。
-能大幅降低开发和沟通成本,简化开发流程,缩短开发周期。
-适合中小型前后端分离的项目,尤其是 创业项目、内部项目、低代码/零代码、小程序、BaaS、Serverless 等。
-
-通过万能通用接口,前端可以定制任何数据、任何结构。
-大部分 HTTP 请求后端再也不用写接口了,更不用写文档了。
-前端再也不用和后端沟通接口或文档问题了。再也不会被文档各种错误坑了。
-后端再也不用为了兼容旧接口写新版接口和文档了。再也不会被前端随时随地没完没了地烦了。
-
-### 特点功能
+
-#### 对于后端
-* 提供万能通用接口,大部分 HTTP API 不用再写
-* 零代码增删改查、各种跨库连表、JOIN 嵌套子查询等
-* 自动生成文档,不用再编写和维护,且自动静态检查
-* 自动校验权限、自动管理版本、自动防 SQL 注入
-* 开放 HTTP API 无需划分版本,始终保持兼容
+##
1. About
+
+APIJSON is a JSON based internet communication protocol and an ORM library
+that largely simplifies the process of back-end API development.
+It also allows users to get data more quickly with self-defined form and fewer endpoints requests.
+
+### Features:
+#### For getting data:
+You can get any data by defining the specific information you want and send it to the server.
+You can get different types of data by making just one request to the server.
+It's very convenient and flexible, and dosen't require different API endpoints with multiple requests.
+It provides CRUD(read and write), Fuzzy Search, Remote Function Calls, etc.
+You can also save duplicate data, see request history, etc.
+
+#### For API design:
+APIJSON largely reduces API developers' workload by reducing most api design and documentation work.
+With APIJSON, client developers will no longer be suffered from possible errors in documents,
+and it saves communication between server developers and client developers about APIs or documentations.
+Server developers no longer need to worry about compatibility of APIs and documents with legacy apps.
+
+
+#### Song Firework-Katy Parry(Modified for APIJSON)
+Do you ever feel like a backend slave
+Repeating CRUD, wanting to make a change?
+Do you ever feel, APIs' so paper thin
+Like a house of cards, one blow from cavin' in?
+Do you ever feel they always complain?
+Urging doc and feedback bugs, even ask your refactoring
+Do you know that there's still a chance for you?
+'Cause there's a powerful tool
+You just gotta depend and configure
+And let it init
+Just start APIs
+They are so easy to try
+'Cause baby, you're a firework
+Come on, show 'em what you're worth
+Make 'em go, "Oh, oh, oh"
+As you give 'em an A-T-M
+Baby, you're a firework
+Come on, let them serve themselves
+Make 'em go, "Oh, oh, oh"
+You're gonna leave 'em all in awe, awe, awe.
-#### 对于前端
-* 不用再向后端催接口、求文档
-* 数据和结构完全定制,要啥有啥
-* 看请求知结果,所求即所得
-* 可一次获取任何数据、任何结构
-* 能去除多余数据,节省流量提高速度
+
-
+**Tired with endless arguments about HTTP API dev or use?**
+**Use APIJSON-the ORM for providing infinity codeless CRUD APIs that fit almost all your needs.**
+**Unfold the Power(In Your Soul) with ⭐Star & Clone.**
-### APIJSON 接口展示
-#### Postman 展示 APIJSON
+### APIJSON Show
+#### Postman test APIJSON

-#### APIAuto 展示 APIJSON
-**使用 APIAuto-机器学习接口工具 来管理和测试 HTTP API 可大幅 减少传参错误、提升联调效率**
-(注意网页工具界面是 APIAuto,里面的 URL+JSON 才是 APIJSON 的 HTTP API):
+#### APIAuto test APIJSON
+Note: The UI is APIAuto, the URL+JSON is APIJSON
6. Author
+
+Check out the author's [github account](https://github.com/TommyLemon) to see more related projects.
+
-更多常见问题及提问前必看
-https://github.com/Tencent/APIJSON/issues/36
-
+If you have any questions or suggestions, you can [create an issue](https://github.com/Tencent/APIJSON/issues) or [send me an e-mail](mailto:tommylemon@qq.com).
-### 注意事项
-**请求参数 JSON 中表名、字段名、关键词及对应的值都是大小写敏感、逗号敏感、分号敏感、空格敏感、换行敏感,
-大部分情况都不允许空格和换行,表名以大写字母开头,不要想当然,请严格按照 [设计规范](https://github.com/Tencent/APIJSON/blob/master/Document.md#3) 来调用 API !**
-[#181](https://github.com/Tencent/APIJSON/issues/181)
-
例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON is the real-time coding-free, powerful and secure ORM')`
例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON is the real-time coding-free, powerful and secure ORM');`
`INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON is the real-time coding-free, powerful and secure ORM' WHERE id=235 AND userId=82001 LIMIT 1`
③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000
② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同 "id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000
③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
+ 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定
① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100
② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用
① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。
⑬ "@null":"key1,key2...",空值键值对,自动插入 key1:null, key2:null ... 并作为有效键值对执行,作为条件时对应 SQL 是 `WHERE tag IS NULL`,作为值时对应 SQL 是 `SET tag = NULL`
⑭ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'`
② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name`
③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id`
④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id`
⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100`
⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7``
② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})
③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
+
diff --git a/Document-English.md b/Document-English.md
deleted file mode 100644
index 62f40b0a..00000000
--- a/Document-English.md
+++ /dev/null
@@ -1,336 +0,0 @@
-### Examples:
-
-#### Get a User
-Request:
-
{
- "User":{
- }
-}
-
-
-[Click here to test](http://apijson.cn:8080/get/{"User":{}})
-
-Response:
-
-
-
-
-#### Get an array of Users
-
-Request:
-
{
- "[]":{
- "count":3, //just get 3 results
- "User":{
- "@column":"id,name" //just get ids and names
- }
- }
-}
-
-
-[Click here to test](http://apijson.cn:8080/get/{"[]":{"count":3,"User":{"@column":"id,name"}}})
-
-Response:
-
-
-
-
-
-#### Get a Moment and its publisher
-Request:
-
-
-
-
-#### Get a Moment list like Twitter tweets
-Request:
-
{
- "[]":{ //get an array
- "page":0, //pagination
- "count":2,
- "Moment":{ //get a Moment
- "content$":"%a%" //filter condition: content contains 'a'
- },
- "User":{
- "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
- "@column":"id,name,head" //get specified keys with the written order
- },
- "Comment[]":{ //get a Comment array, and unwrap Comment object
- "count":2,
- "Comment":{
- "momentId@":"[]/Moment/id" //Comment.momentId = Moment.id, full reference path
- }
- }
- }
-}
-
-
-[Click here to test](http://apijson.cn:8080/get/{"[]":{"page":0,"count":2,"Moment":{"content$":"%2525a%2525"},"User":{"id@":"%252FMoment%252FuserId","@column":"id,name,head"},"Comment[]":{"count":2,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
-
-Response:
-
-
-
-
-
-
-[Test it online](http://apijson.cn/api)
-
-
-
-
-## API Design Rules
-
-### 1. Methods and API endpoints
-
- Methods | URL | Request | Response
------------- | ------------ | ------------ | ------------
-**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } }
Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
-**HEAD**: A general way to get counts. You can use dev tools to make edits in a web browser. | base_url/head/ | { TableName:{ … } } {…} are conditions.
Eg. Get the number of Moments posted by the user with `id = 38710`: { "Moment":{ "userId":38710 } } | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
-**GETS**: Get data with high security and confidentiality. Eg. bank accounts, birth date. | base_url/gets/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **GET**. | Same as **GET**.
-**HEADS**: Get counts of confidential data(eg. bank account).| base_url/heads/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **HEAD**. | Same as **HEAD**.
-**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user.
Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
-**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`.
Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | Same as **POST**.
-**DELETE**: Delete data. | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } You can also add multiple id as `id{}`.
Or Delete contents with multiple id: { "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" } | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } Eg. { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
-
-**Note**:
-1. TableName means the name of the table where you get data. It’ll respond with a JSON Object(the form is {....})with columns inside.
-2. `"tag":tag` is needed when methods are not GET or HEAD. The tag after the colon is the key in JSON Object of making requests. Generally, it’s the name of the table you’re looking for.
-3. GET, HEAD are methods for general data requests.They support versatile JSON Object structure. Other methods are used for requesting confidential data and the requesting JSON Object needs to be in the same form/order as that in the database. Otherwise, the request shall be denied.
-4. GETS and GET, HEADS and HEAD return the same type of data. But the request form is a little different.
-5. For HTTP, all API methods (get,gets,head,heads,post,put,delete) make requests with HTTP POST.
-6. All JSON Objects here are with {...} form. You can put items or objects in it.
-7. Each object in the database has a unique address.
-
-
-
-### 2. Keyswords in URL parameters
-
- Functions | Key-value pairs | Examples
------------- | ------------ | ------------
- Get data in arrays | `"key[]":{}` The part after the colon is a JSONObject. *key* is optional. When *key* is the same as the table name , the JSONObject will be in a simplified form. For example, `{Table:{Content}}` will be written as `{Content}`.| [{"User[]":{"User":{}}}](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{}}}) It is used for getting data from a user. Here, key and tablename are all "User", then `{"User":{"id", ...}}` will be written as `{"id", ...}`
- Get data that meets specific conditions | `"key{}":[]` The part after the colon is a JSONArray with conditions inside.| ["id{}":[38710,82001,70793]](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":[38710,82001,70793]}}}) In SQL, this would be `id IN(38710,82001,70793)`. It means getting data with id equals 38710,82001,70793.
- Get data with comparison operation| `"key{}":"condition0,condition1..."` Conditions can be any SQL comparision operation. Use''to include any non-number characters.| ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}) In SQL, it'd be `id<=80000 OR id>90000`, which means get User array with id\<=80000 \| id>90000
- Get data that contains an element | `"key<>":Object` => `"key<>":[Object]` *key* must be a JSONArray while *Object* cannot be JSON.| ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}) In SQL, this would be `json_contains(contactIdList,38710)`. It means find data of the User whose contactList contains 38710.
- See if it exists |`"key}{@":{` `"from":"Table",` `"Table":{ ... }` `}`
}{ means EXISTS. *key* is the one you want to check. Here is a *Subquery* in it, see specifications below for more information. | ["id}{@":{ "from":"Comment", "Comment":{ "momentId":15 } }](http://apijson.cn:8080/get/{"User":{"id}{@":{"from":"Comment","Comment":{"momentId":15}}}}) WHERE EXISTS(SELECT * FROM Comment WHERE momentId=15)
- Include functions in parameters | `"key()":"function (key0,key1...)"` This will trigger the back-end `function(JSONObject request, String key0, String key1...)` to get or testify data. Use - and + to show the order of priority: analyze key-() > analyze the current object > analyze key() > analyze child object > analyze key+()| ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}) This will use function boolean isContain(JSONObject request, String array, String value). In this case, client will get "isPraised":true(In this case, client use function to testify if a user clicked ‘like’ button for a Moment.)
- Refer a value | `"key@":"key0/key1/.../refKey"` Use / to show path. The part before the colon is the key that wants to refer. The path after the colon starts with the parent level of the key.| ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) In this example, the value of id in User refer to the *userId* in *Moment*, which means `User.id = Moment.userId`. After the request is sent, `"id@":"/Moment/userId"` will be `"id":38710`.
- Subquery | `"key@":{` `"range":"ALL",` `"from":"Table",` `"Table":{ ... }` `}` *range* can be ALL, ANY. *from* means which table you want to query. It’s very similar to how you query in SQL. You can also use *count*, *join*, etc. | ["id@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) `WHERE id=(SELECT min(userId) FROM Comment)`.
- Fuzzy matching | `"key$":"SQL search expressions"` => `"key$":["SQL search expressions"]` Any SQL search expressions.Eg.%key%(include key), key%(start with key),%k%e%y%(include k, e, y). % means any characters. | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}) In SQL, it's `name LIKE '%m%'`, meaning that get User with ‘m’ in name.
- Regular Expression| `"key~":"regular expression"` => `"key~":["regular expression"]` It can be any regular expressions.Eg. ^[0-9]+$ ,*~ not case sensitive, advanced search is applicable.| ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}) In SQL, it's `name REGEXP '^[0-9]+$'`.
- Get data in a range| `"key%":"start,end"` => `"key%":["start,end"]` The data type of start and end can only be either Boolean, Number or String. Eg. "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"]. It's used for getting data from a specific time range. | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}) In SQL, it's `date BETWEEN '2017-10-01' AND '2018-10-01'`, meaning to get User data that registered between 2017-10-01 and 2018-10-01.
- Make an alias | `"name:alias"` this changes name to alias in returning results. It’s applicable to column, tableName, SQL Functions, etc. but only in GET, HEAD requests. | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}) In SQL, it's `toId AS parentId`. It'll return `parentId` instead of `toId`.
For @key format like "lc_wai6b3vk2:(lc_wai6b3vk)", it means renaming field lc_wai6b3vk2 to lc_wai6b3vk, commonly used for field renaming scenarios. Example: { "lc_sinan_ba074fbb": { "lc_wai6b3vk": "11", "lc_wai6b3vk2": "22", "@combine": "lc_wai6b3vk \\| lc_wai6b3vk2", "@key": "lc_wai6b3vk2:(lc_wai6b3vk)" } } corresponds to SQL `(lc_wai6b3vk = '11' OR lc_wai6b3vk2 = '22')`, but the lc_wai6b3vk2 field will be renamed and displayed as lc_wai6b3vk in the returned result
- Add / expand an item | `"key+":Object` The type of Object is decided by *key*. Types can be Number, String, JSONArray. Froms are 82001,"apijson",["url0","url1"] respectively. It’s only applicable to PUT request.| "praiseUserIdList+":[82001]. In SQL, it's `json_insert(praiseUserIdList,82001)`. Add an *id* that praised the Moment.
- Delete / decrease an item | `"Key-":Object` It’s the contrary of "key+" | "balance-":100.00. In SQL, it's `balance = balance - 100.00`, meaning there's 100 less in balance.
- Operations | &, \|, ! They're used in logic operations. It’s the same as AND, OR, NOT in SQL respectively. By default, for the same key, it’s ‘\|’ (OR)operation among conditions; for different keys, the default operation among conditions is ‘&’(AND). | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}) In SQL, it's `id>80000 AND id<=90000`, meaning *id* needs to be id>80000 & id<=90000
② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}) It's the same as "id{}":">90000,<=80000". In SQL, it's `id>80000 OR id<=90000`, meaning that *id* needs to be id>90000 \| id<=80000
③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}) In SQL, it's `id NOT IN(82001,38710)`, meaning id needs to be ! (id=82001 \| id=38710).
- Keywords in an Array: It can be self-defined. | As for `"key":Object`, *key* is the keyword of *{}* in *"[]":{}*. The type of *Object* is up to *key*.
① `"count":Integer` It's used to count the number. The default largest number is 100.
② `"page":Integer` It’s used for getting data from which page, starting from 0. The default largest number is 100. It’s usually used with COUNT.
③ `"query":Integer` Get the number of items that match conditions When to get the object, the integer should be 0; when to get the total number, it’s 1; when both above, it’s 2. You can get the total number with keyword total. It can be referred to other values. Eg. `"total@":"/[]/total"` Put it as the same level of query. *Query* and *total* are used in GET requests just for convenience. Generally, HEAD request is for getting numbers like the total number.
④ `"join":"&/Table0,Join tables: "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "@" - APP JOIN Where @ APP JOIN is in application layer.It’ll get all the keys in tables that refKeys in result tables are referred to, like refKeys:[value0, value1….]. Then, as the results get data according to `key=$refKey` a number of times (COUNT), it uses key `IN($refKeys)` to put these counts together in just one SQL query, in order to improve the performance. Other JOIN functions are the same as those in SQL. `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` will return `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey`
⑤ `"otherKey":Object` Self-defined keyword other than those that already in the system. It also returns with self-defined keywords.| ① Get User arrays with maximum of 5: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}})
② Look into User arrays on page 3. Show 5 of them each page. ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}})
③ Get User Arrays and count the total number of Users: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total"](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal"}) Questions like total page numbers or if there's next page can be solved by total,count,page functions, Total page number: `int totalPage = Math.ceil(total / count)` If this is the last page: `boolean hasNextPage = total > count*page` If this is the first page: `boolean isFirstPage = page <= 0` If it's the last page: `boolean isLastPage = total <= count*page` ...
④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join": "&/User,\ "Moment":{}, "User":{ "name~":"t", "id@": "/Moment/userId" }, "Comment":{ "momentId@": "/Moment/id" } }](http://apijson.cn:8080/get/{"[]":{"count":5,"join":"&%252FUser,\<%252FComment","Moment":{"@column":"id,userId,content"},"User":{"name~":"t","id@":"%252FMoment%252FuserId","@column":"id,name,head"},"Comment":{"momentId@":"%252FMoment%252Fid","@column":"id,momentId,content"}}})
⑤ Add the current user to every level: ["User":{}, "[]":{ "name@":"User/name", //self-defined keyword "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
- Keywords in Objects: It can be self-defined. | `"@key":Object` @key is the keyword of {} in Table:{}. The type of Object is decided by @key
① `"@combine":"&key0,&key1,\|key2,key3,` `!key4,!key5,&key6,key7..."` First, it’ll group data with same operators. Within one group, it operates from left to right. Then it’ll follow the order of & \| ! to do the operation. Different groups are connected with &. So the expression above will be : (key0 & key1 & key6 & other key) & (key2 \| key3 \| key7) & !(key4 \| key5) \| is optional.
② `"@column":"column;function(arg)..."` Return with specific columns.
③ `"@order":"column0+,column1-..."` Decide the order of returning results:
④ `"@group":"column0,column1..."` How to group data. If @column has declared Table id, this id need to be included in @group. In other situations, at least one of the following needs to be done: 1.Group id is declared in @column 2.Primary Key of the table is declared in @group.
⑤ `@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..."` Add conditions on return results with @having. Usually working with@group, it’s declared in @column.
⑥ `"@schema":"sys"` Can be set as default setting.
⑦ `"@database":"POSTGRESQL"` Get data from a different database.Can be set as default setting.
⑧ `"@role":"OWNER"` Get information of the user, including UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, Can be set as default setting. You can self-define a new role or rewrite a role. Use`Verifier.verify` etc. to self-define validation methods.
⑨ `"@explain":true` Profiling. Can be set as default setting.
⑩ `"@otherKey":Object` Self-define keyword | ① Search *Users* that *name* or *tag* contains the letter "a": ["name~":"a", "tag~":"a", "@combine":"name~,tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~,tag~"}}})
② Only search column id,sex,name and return with the same order: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}})
③ Search Users that have descending order of name and default order of id: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}})
④ Search Moment grouped with userId: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}})
⑤ Search Moments that id equals or less than 100 and group with userId: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) You can also define the name of the returned function: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}})
⑥ Check Users table in sys: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}})
⑦ Check Users table in PostgreSQL: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL"}})
⑧ Check the current user's activity: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}})
⑨ Turn on profiling: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}})
⑩ Get the No.0 picture from pictureList: ["@position":0, //self-defined keyword "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
- global keyword. | It is a keyword inside the outermost object {}. Among them, @database, @schema, @datasource, @role, and @explain are basically the same as object keywords, see the above description, the difference is that the global keywords will be automatically inserted in each table object as the default value.
① "tag": String, the following tag is the identifier of the JSON structure matching the request in non-GET or HEAD requests, generally it is the name of the Table to be queried or the array Table[] or Table:[] corresponding to the name, determined by the backend specified in the Request table.
② "version": Integer, the interface version. If the version is not passed, null or <=0, the highest version will be used. If other valid values are passed, the lowest version closest to it will be used, which is specified in the backend Request table.
③ "format": Boolean, formatted to return the key of the Response JSON, generally converting TableName to tableName, TableName[] to tableNameList, Table:alias to alias, TableName-key[] to tableNameKeyList and other camelcase formats. | ① Check private information:: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})
② Use the version 1 interface to check private information:: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})

-
-
-#### 获取用户列表
-请求:
+#### Get an array of Users
+
+Request:
{
"[]":{
- "count":3, //只要3个
+ "count":3, //just get 3 results
"User":{
- "@column":"id,name" //只要id,name这两个字段
+ "@column":"id,name" //just get ids and names
}
}
}
-[点击这里测试](http://apijson.cn:8080/get/{"[]":{"count":3,"User":{"@column":"id,name"}}})
+[Click here to test](http://apijson.cn:8080/get/{"[]":{"count":3,"User":{"@column":"id,name"}}})
-返回:
+Response:

-
-#### 获取动态及发布者用户
-请求:
+#### Get a Moment and its publisher
+Request:
-[点击这里测试](http://apijson.cn:8080/get/{"Moment":{},"User":{"id@":"Moment%252FuserId"}})
+[Click here to test](http://apijson.cn:8080/get/{"Moment":{},"User":{"id@":"Moment%252FuserId"}})
-返回:
+Response:
{
"Moment":{
"id":12,
@@ -152,32 +130,32 @@ https://github.com/Tencent/APIJSON
-#### 获取类似微信朋友圈的动态列表
-请求:
+#### Get a Moment list like Twitter tweets
+Request:
{
- "[]":{ //请求一个数组
- "page":0, //数组条件
+ "[]":{ //get an array
+ "page":0, //pagination
"count":2,
- "Moment":{ //请求一个名为Moment的对象
- "content$":"%a%" //对象条件,搜索content中包含a的动态
+ "Moment":{ //get a Moment
+ "content$":"%a%" //filter condition: content contains 'a'
},
"User":{
- "id@":"/Moment/userId", //User.id = Moment.userId 缺省引用赋值路径,从所处容器的父容器路径开始
- "@column":"id,name,head" //指定返回字段
+ "id@":"/Moment/userId", //User.id = Moment.userId, short reference path,starts from grandparents path
+ "@column":"id,name,head" //get specified keys with the written order
},
- "Comment[]":{ //请求一个名为Comment的数组,并去除Comment包装
+ "Comment[]":{ //get a Comment array, and unwrap Comment object
"count":2,
"Comment":{
- "momentId@":"[]/Moment/id" //Comment.momentId = Moment.id 完整引用赋值路径
+ "momentId@":"[]/Moment/id" //Comment.momentId = Moment.id, full reference path
}
}
}
}
-[点击这里测试](http://apijson.cn:8080/get/{"[]":{"page":0,"count":2,"Moment":{"content$":"%2525a%2525"},"User":{"id@":"%252FMoment%252FuserId","@column":"id,name,head"},"Comment[]":{"count":2,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
+[Click here to test](http://apijson.cn:8080/get/{"[]":{"page":0,"count":2,"Moment":{"content$":"%2525a%2525"},"User":{"id@":"%252FMoment%252FuserId","@column":"id,name,head"},"Comment[]":{"count":2,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
-返回:
+Response:

-[在线测试](http://apijson.cn/api)
+[Test it online](http://apijson.cn/api)
3.设计规范
+ Methods | URL | Request | Response
+------------ | ------------ | ------------ | ------------
+**GET**: A general way to get data. You can use dev tools to make edits in a web browser. | base_url/get/ | { TableName:{ //Add contiditions here. } }
Eg. To get a Moment with `id = 235`: { "Moment":{ "id":235 } } | { TableName:{ ... }, "code":200, "msg":"success" } Eg. { "Moment":{ "id":235, "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "code":200, "msg":"success" }
+**HEAD**: A general way to get counts. You can use dev tools to make edits in a web browser. | base_url/head/ | { TableName:{ … } } {…} are conditions.
Eg. Get the number of Moments posted by the user with `id = 38710`: { "Moment":{ "userId":38710 } } | { TableName:{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "count":10 }, "code":200, "msg":"success" }
+**GETS**: Get data with high security and confidentiality. Eg. bank accounts, birth date. | base_url/gets/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **GET**. | Same as **GET**.
+**HEADS**: Get counts of confidential data(eg. bank account).| base_url/heads/ | You need to add `"tag":tag` with the same level of `Moment:{}`. Others are the same as **HEAD**. | Same as **HEAD**.
+**POST**: Add new data. | base_url/post/ | { TableName:{ … }, "tag":tag } The id in {...} is generated automatically when table is built and can’t be set by the user.
Eg. A user with `id = 38710` posts a new Moment: { "Moment":{ "userId":38710, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } Eg. { "Moment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
+**PUT**: Make changes to a specific item. Only change the part sent to server. | base_url/put/ | { TableName:{ "id":id, … }, "tag":tag } You can also add multiple id as `id{}`.
Eg. Make changes to Moment's content with id= 235: { "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" } | Same as **POST**.
+**DELETE**: Delete data. | base_url/delete/ | { TableName:{ "id":id }, "tag":tag } You can also add multiple id as `id{}`.
Or Delete contents with multiple id: { "Comment":{ "id{}":[100,110,120] }, "tag":"Comment[]" } | { TableName:{ "code":200, "msg":"success", "id[]":[100,110,120] "count":3 }, "code":200, "msg":"success" } Eg. { "Comment":{ "code":200, "msg":"success", "id[]":[100,110,120], "count":3 }, "code":200, "msg":"success" }
+
+**Note**:
+1. TableName means the name of the table where you get data. It’ll respond with a JSON Object(the form is {....})with columns inside.
+2. `"tag":tag` is needed when methods are not GET or HEAD. The tag after the colon is the key in JSON Object of making requests. Generally, it’s the name of the table you’re looking for.
+3. GET, HEAD are methods for general data requests.They support versatile JSON Object structure. Other methods are used for requesting confidential data and the requesting JSON Object needs to be in the same form/order as that in the database. Otherwise, the request shall be denied.
+4. GETS and GET, HEADS and HEAD return the same type of data. But the request form is a little different.
+5. For HTTP, all API methods (get,gets,head,heads,post,put,delete) make requests with HTTP POST.
+6. All JSON Objects here are with {...} form. You can put items or objects in it.
+7. Each object in the database has a unique address.
-###
例如当前登录用户 38710 发布一个新 Comment: [{ "Comment":{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Comment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment":{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Comment"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(38710,12,'APIJSON is the real-time coding-free, powerful and secure ORM')`
例如当前登录用户 82001 发布 2 个 Comment: [{ "Comment[]":[{ "momentId":12, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, { "momentId":15, "content":"APIJSON is a JSON transmision protocol." }], "tag":"Comment:[]" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fpost&type=JSON&json={"Comment[]":[{"momentId":12,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},{"momentId":15,"content":"APIJSON%20is%20a%20JSON%20transmision%20protocol."}],"tag":"Comment:[]"}) 后端校验通过后自动解析为 SQL 并执行: `INSERT INTO Comment(userId,momentId,content) VALUES(82001,12,'APIJSON is the real-time coding-free, powerful and secure ORM');`
`INSERT INTO Comment(userId,momentId,content) VALUES(82001,15,'APIJSON is a JSON transmision protocol.');` | 单个: { TableName:{ "code":200, "msg":"success", "id":38710 }, "code":200, "msg":"success" } 例如 { "Comment":{ "code":200, "msg":"success", "id":120 }, "code":200, "msg":"success" }
例如当前登录用户 82001 修改 id = 235 的 Moment 的 content: [{ "Moment":{ "id":235, "content":"APIJSON is the real-time coding-free, powerful and secure ORM" }, "tag":"Moment" }](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fput&type=JSON&json={"Moment":{"id":235,"content":"APIJSON%20is%20the%20Real-Time%20coding-free,%20powerful%20and%20secure%20ORM."},"tag":"Moment"}) 后端校验通过后自动解析为 SQL 并执行: `UPDATE Moment SET content='APIJSON is the real-time coding-free, powerful and secure ORM' WHERE id=235 AND userId=82001 LIMIT 1`
③ ! 可单独使用,如 "key!":Object,也可像 &,\| 一样配合其他功能符使用 "key!":null 无效,null 值会导致整个键值对被忽略解析,可以用 "key{}":"!=null" 替代, "key":null 同理,用 "key{}":"=null" 替代。 | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}),对应SQL是`id>80000 AND id<=90000`,即id满足id>80000 & id<=90000
② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}),同 "id{}":">90000,<=80000",对应 SQL 是`id>90000 OR id<=80000`,即 id 满足 id>90000 \| id<=80000
③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}),对应 SQL 是`id NOT IN(82001,38710)`,即 id 满足 ! (id=82001 \| id=38710),可过滤黑名单的消息
- 数组关键词,可自定义 | "key":Object,key为 "[]":{} 中 {} 内的关键词,Object 的类型由 key 指定
① "count":5,查询数量,0 表示最大值,默认值为 10,默认最大值为 100
② "page":1,查询页码,从 0 开始,默认值为 0,默认最大值为 100,一般和 count 一起用
① "@combine":"key0 \| (key1 & (key2 \| !key3))...",条件组合方式,最终按 (其它key条件 AND 连接) AND (key0条件 OR (key1条件 AND (key2条件 OR (NOT key3条件)))) 这种方式连接,其中 "其它key" 是指与 @combine 在同一对象,且未被它声明的条件 key,默认都是 & 连接。注意不要缺少或多余任何一个空格。
⑬ "@null":"key1,key2...",空值键值对,自动插入 key1:null, key2:null ... 并作为有效键值对执行,作为条件时对应 SQL 是 `WHERE tag IS NULL`,作为值时对应 SQL 是 `SET tag = NULL`
⑭ "@otherKey":Object,自定义关键词,名称和以上系统关键词不一样,且原样返回上传的值 | ① 搜索 name 或 tag 任何一个字段包含字符 a 的 User 列表: ["name~":"a", "tag~":"a", "@combine":"name~ \| tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~%20%7C%20tag~"}}}) 对应SQL是`name REGEXP 'a' OR tag REGEXP 'a'`
② 只查询 id,sex,name 这几列并且请求结果也按照这个顺序: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}}) 对应 SQL 是`SELECT id,sex,name`
③ 查询按 name 降序、id 默认顺序 排序的 User 数组: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}}) 对应 SQL 是`ORDER BY name DESC,id`
④ 查询按 userId 分组的 Moment 数组: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}}) 对应 SQL 是`GROUP BY userId,id`
⑤ 查询 按 userId 分组、id 最大值>=100 的 Moment 数组: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100` 还可以指定函数返回名: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}}) 对应 SQL 是`SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING (maxId)>=100`
⑫ 统计最近一周偶数 userId 的数量 ["@column":"date;left(date,10):day;sum(if(userId%2=0,1,0))", "@group":"day", "@having":"to_days(now())-to_days(\`date\`)<=7", "@raw":"@column,@having"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@column":"date%3bleft(date,10):day%3bsum(if(userId%252=0,1,0))","@group":"day","@having":"to_days(now())-to_days(\`date\`)<=7","@raw":"@column,@having"}}}) 对应 SQL 是``SELECT date, left(date,10) AS day, sum(if(userId%2=0,1,0)) ... GROUP BY day HAVING to_days(now())-to_days(`date`)<=7``
② 使用第 1 版接口查隐私信息: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})
③ 格式化朋友圈接口返回 JSON 中的 key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
+ Get data in arrays | `"key[]":{}` The part after the colon is a JSONObject. *key* is optional. When *key* is the same as the table name , the JSONObject will be in a simplified form. For example, `{Table:{Content}}` will be written as `{Content}`.| [{"User[]":{"User":{}}}](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{}}}) It is used for getting data from a user. Here, key and tablename are all "User", then `{"User":{"id", ...}}` will be written as `{"id", ...}`
+ Get data that meets specific conditions | `"key{}":[]` The part after the colon is a JSONArray with conditions inside.| ["id{}":[38710,82001,70793]](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":[38710,82001,70793]}}}) In SQL, this would be `id IN(38710,82001,70793)`. It means getting data with id equals 38710,82001,70793.
+ Get data with comparison operation| `"key{}":"condition0,condition1..."` Conditions can be any SQL comparision operation. Use''to include any non-number characters.| ["id{}":"<=80000,\>90000"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"id{}":"<=80000,\>90000"}}}) In SQL, it'd be `id<=80000 OR id>90000`, which means get User array with id\<=80000 \| id>90000
+ Get data that contains an element | `"key<>":Object` => `"key<>":[Object]` *key* must be a JSONArray while *Object* cannot be JSON.| ["contactIdList<\>":38710](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"contactIdList<\>":38710}}}) In SQL, this would be `json_contains(contactIdList,38710)`. It means find data of the User whose contactList contains 38710.
+ See if it exists |`"key}{@":{` `"from":"Table",` `"Table":{ ... }` `}`
}{ means EXISTS. *key* is the one you want to check. Here is a *Subquery* in it, see specifications below for more information. | ["id}{@":{ "from":"Comment", "Comment":{ "momentId":15 } }](http://apijson.cn:8080/get/{"User":{"id}{@":{"from":"Comment","Comment":{"momentId":15}}}}) WHERE EXISTS(SELECT * FROM Comment WHERE momentId=15)
+ Include functions in parameters | `"key()":"function (key0,key1...)"` This will trigger the back-end `function(JSONObject request, String key0, String key1...)` to get or testify data. Use - and + to show the order of priority: analyze key-() > analyze the current object > analyze key() > analyze child object > analyze key+()| ["isPraised()":"isContain(praiseUserIdList,userId)"](http://apijson.cn:8080/get/{"Moment":{"id":301,"isPraised()":"isContain(praiseUserIdList,userId)"}}) This will use function boolean isContain(JSONObject request, String array, String value). In this case, client will get "isPraised":true(In this case, client use function to testify if a user clicked ‘like’ button for a Moment.)
+ Refer a value | `"key@":"key0/key1/.../refKey"` Use / to show path. The part before the colon is the key that wants to refer. The path after the colon starts with the parent level of the key.| ["Moment":{ "userId":38710 }, "User":{ "id@":"/Moment/userId" }](http://apijson.cn:8080/get/{"Moment":{"userId":38710},"User":{"id@":"%252FMoment%252FuserId"}}) In this example, the value of id in User refer to the *userId* in *Moment*, which means `User.id = Moment.userId`. After the request is sent, `"id@":"/Moment/userId"` will be `"id":38710`.
+ Subquery | `"key@":{` `"range":"ALL",` `"from":"Table",` `"Table":{ ... }` `}` *range* can be ALL, ANY. *from* means which table you want to query. It’s very similar to how you query in SQL. You can also use *count*, *join*, etc. | ["id@":{ "from":"Comment", "Comment":{ "@column":"min(userId)" } }](http://apijson.cn:8080/get/{"User":{"id@":{"from":"Comment","Comment":{"@column":"min(userId)"}}}}) `WHERE id=(SELECT min(userId) FROM Comment)`.
+ Fuzzy matching | `"key$":"SQL search expressions"` => `"key$":["SQL search expressions"]` Any SQL search expressions.Eg.%key%(include key), key%(start with key),%k%e%y%(include k, e, y). % means any characters. | ["name$":"%m%"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name$":"%2525m%2525"}}}) In SQL, it's `name LIKE '%m%'`, meaning that get User with ‘m’ in name.
+ Regular Expression| `"key~":"regular expression"` => `"key~":["regular expression"]` It can be any regular expressions.Eg. ^[0-9]+$ ,*~ not case sensitive, advanced search is applicable.| ["name~":"^[0-9]+$"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"name~":"^[0-9]%252B$"}}}) In SQL, it's `name REGEXP '^[0-9]+$'`.
+ Get data in a range| `"key%":"start,end"` => `"key%":["start,end"]` The data type of start and end can only be either Boolean, Number or String. Eg. "2017-01-01,2019-01-01" ,["1,90000", "82001,100000"]. It's used for getting data from a specific time range. | ["date%":"2017-10-01,2018-10-01"](http://apijson.cn:8080/get/{"User[]":{"count":3,"User":{"date%2525":"2017-10-01,2018-10-01"}}}) In SQL, it's `date BETWEEN '2017-10-01' AND '2018-10-01'`, meaning to get User data that registered between 2017-10-01 and 2018-10-01.
+ Make an alias | `"name:alias"` this changes name to alias in returning results. It’s applicable to column, tableName, SQL Functions, etc. but only in GET, HEAD requests. | ["@column":"toId:parentId"](http://apijson.cn:8080/get/{"Comment":{"@column":"id,toId:parentId","id":51}}) In SQL, it's `toId AS parentId`. It'll return `parentId` instead of `toId`.
For @key format like "lc_wai6b3vk2:(lc_wai6b3vk)", it means renaming field lc_wai6b3vk2 to lc_wai6b3vk, commonly used for field renaming scenarios. Example: { "lc_sinan_ba074fbb": { "lc_wai6b3vk": "11", "lc_wai6b3vk2": "22", "@combine": "lc_wai6b3vk \\| lc_wai6b3vk2", "@key": "lc_wai6b3vk2:(lc_wai6b3vk)" } } corresponds to SQL `(lc_wai6b3vk = '11' OR lc_wai6b3vk2 = '22')`, but the lc_wai6b3vk2 field will be renamed and displayed as lc_wai6b3vk in the returned result
+ Add / expand an item | `"key+":Object` The type of Object is decided by *key*. Types can be Number, String, JSONArray. Froms are 82001,"apijson",["url0","url1"] respectively. It’s only applicable to PUT request.| "praiseUserIdList+":[82001]. In SQL, it's `json_insert(praiseUserIdList,82001)`. Add an *id* that praised the Moment.
+ Delete / decrease an item | `"Key-":Object` It’s the contrary of "key+" | "balance-":100.00. In SQL, it's `balance = balance - 100.00`, meaning there's 100 less in balance.
+ Operations | &, \|, ! They're used in logic operations. It’s the same as AND, OR, NOT in SQL respectively. By default, for the same key, it’s ‘\|’ (OR)operation among conditions; for different keys, the default operation among conditions is ‘&’(AND). | ① ["id&{}":">80000,<=90000"](http://apijson.cn:8080/head/{"User":{"id&{}":">80000,<=90000"}}) In SQL, it's `id>80000 AND id<=90000`, meaning *id* needs to be id>80000 & id<=90000
② ["id\|{}":">90000,<=80000"](http://apijson.cn:8080/head/{"User":{"id\|{}":">90000,<=80000"}}) It's the same as "id{}":">90000,<=80000". In SQL, it's `id>80000 OR id<=90000`, meaning that *id* needs to be id>90000 \| id<=80000
③ ["id!{}":[82001,38710]](http://apijson.cn:8080/head/{"User":{"id!{}":[82001,38710]}}) In SQL, it's `id NOT IN(82001,38710)`, meaning id needs to be ! (id=82001 \| id=38710).
+ Keywords in an Array: It can be self-defined. | As for `"key":Object`, *key* is the keyword of *{}* in *"[]":{}*. The type of *Object* is up to *key*.
① `"count":Integer` It's used to count the number. The default largest number is 100.
② `"page":Integer` It’s used for getting data from which page, starting from 0. The default largest number is 100. It’s usually used with COUNT.
③ `"query":Integer` Get the number of items that match conditions When to get the object, the integer should be 0; when to get the total number, it’s 1; when both above, it’s 2. You can get the total number with keyword total. It can be referred to other values. Eg. `"total@":"/[]/total"` Put it as the same level of query. *Query* and *total* are used in GET requests just for convenience. Generally, HEAD request is for getting numbers like the total number.
④ `"join":"&/Table0,Join tables: "\<" - LEFT JOIN ">" - RIGHT JOIN "&" - INNER JOIN "\|" - FULL JOIN "!" - OUTER JOIN "@" - APP JOIN Where @ APP JOIN is in application layer.It’ll get all the keys in tables that refKeys in result tables are referred to, like refKeys:[value0, value1….]. Then, as the results get data according to `key=$refKey` a number of times (COUNT), it uses key `IN($refKeys)` to put these counts together in just one SQL query, in order to improve the performance. Other JOIN functions are the same as those in SQL. `"join":"`"MainTable":{},` `"ViceTable":{"key@":"/MainTable/refKey"}` will return `MainTable LEFT JOIN ViceTable` `ON ViceTable.key=MainTable.refKey`
⑤ `"otherKey":Object` Self-defined keyword other than those that already in the system. It also returns with self-defined keywords.| ① Get User arrays with maximum of 5: ["count":5](http://apijson.cn:8080/get/{"[]":{"count":5,"User":{}}})
② Look into User arrays on page 3. Show 5 of them each page. ["count":5, "page":3](http://apijson.cn:8080/get/{"[]":{"count":5,"page":3,"User":{}}})
③ Get User Arrays and count the total number of Users: ["[]":{ "query":2, "User":{} }, "total@":"/[]/total"](http://apijson.cn:8080/get/{"[]":{"query":2,"count":5,"User":{}},"total@":"%252F[]%252Ftotal"}) Questions like total page numbers or if there's next page can be solved by total,count,page functions, Total page number: `int totalPage = Math.ceil(total / count)` If this is the last page: `boolean hasNextPage = total > count*page` If this is the first page: `boolean isFirstPage = page <= 0` If it's the last page: `boolean isLastPage = total <= count*page` ...
④ Moment INNER JOIN User LEFT JOIN Comment: ["[]":{ "join": "&/User,\ "Moment":{}, "User":{ "name~":"t", "id@": "/Moment/userId" }, "Comment":{ "momentId@": "/Moment/id" } }](http://apijson.cn:8080/get/{"[]":{"count":5,"join":"&%252FUser,\<%252FComment","Moment":{"@column":"id,userId,content"},"User":{"name~":"t","id@":"%252FMoment%252FuserId","@column":"id,name,head"},"Comment":{"momentId@":"%252FMoment%252Fid","@column":"id,momentId,content"}}})
⑤ Add the current user to every level: ["User":{}, "[]":{ "name@":"User/name", //self-defined keyword "Moment":{} }](http://apijson.cn:8080/get/{"User":{},"[]":{"name@":"User%252Fname","Moment":{}}})
+ Keywords in Objects: It can be self-defined. | `"@key":Object` @key is the keyword of {} in Table:{}. The type of Object is decided by @key
① `"@combine":"&key0,&key1,\|key2,key3,` `!key4,!key5,&key6,key7..."` First, it’ll group data with same operators. Within one group, it operates from left to right. Then it’ll follow the order of & \| ! to do the operation. Different groups are connected with &. So the expression above will be : (key0 & key1 & key6 & other key) & (key2 \| key3 \| key7) & !(key4 \| key5) \| is optional.
② `"@column":"column;function(arg)..."` Return with specific columns.
③ `"@order":"column0+,column1-..."` Decide the order of returning results:
④ `"@group":"column0,column1..."` How to group data. If @column has declared Table id, this id need to be included in @group. In other situations, at least one of the following needs to be done: 1.Group id is declared in @column 2.Primary Key of the table is declared in @group.
⑤ `@having":"function0(...)?value0;function1(...)?value1;function2(...)?value2..."` Add conditions on return results with @having. Usually working with@group, it’s declared in @column.
⑥ `"@schema":"sys"` Can be set as default setting.
⑦ `"@database":"POSTGRESQL"` Get data from a different database.Can be set as default setting.
⑧ `"@role":"OWNER"` Get information of the user, including UNKNOWN,LOGIN,CONTACT,CIRCLE,OWNER,ADMIN, Can be set as default setting. You can self-define a new role or rewrite a role. Use`Verifier.verify` etc. to self-define validation methods.
⑨ `"@explain":true` Profiling. Can be set as default setting.
⑩ `"@otherKey":Object` Self-define keyword | ① Search *Users* that *name* or *tag* contains the letter "a": ["name~":"a", "tag~":"a", "@combine":"name~,tag~"](http://apijson.cn:8080/get/{"User[]":{"count":10,"User":{"@column":"id,name,tag","name~":"a","tag~":"a","@combine":"name~,tag~"}}})
② Only search column id,sex,name and return with the same order: ["@column":"id,sex,name"](http://apijson.cn:8080/get/{"User":{"@column":"id,sex,name","id":38710}})
③ Search Users that have descending order of name and default order of id: ["@order":"name-,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"User":{"@column":"name,id","@order":"name-,id"}}})
④ Search Moment grouped with userId: ["@group":"userId,id"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":%7B"@column":"userId,id","@group":"userId,id"}}})
⑤ Search Moments that id equals or less than 100 and group with userId: ["@column":"userId;max(id)", "@group":"userId", "@having":"max(id)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id)","@group":"userId","@having":"max(id)>=100"}}}) You can also define the name of the returned function: ["@column":"userId;max(id):maxId", "@group":"userId", "@having":"(maxId)>=100"](http://apijson.cn:8080/get/{"[]":{"count":10,"Moment":{"@column":"userId%253Bmax(id):maxId","@group":"userId","@having":"(maxId)>=100"}}})
⑥ Check Users table in sys: ["@schema":"sys"](http://apijson.cn:8080/get/{"User":{"@schema":"sys"}})
⑦ Check Users table in PostgreSQL: ["@database":"POSTGRESQL"](http://apijson.cn:8080/get/{"User":{"@database":"POSTGRESQL"}})
⑧ Check the current user's activity: ["@role":"OWNER"](http://apijson.cn:8080/get/{"[]":{"Moment":{"@role":"OWNER"}}})
⑨ Turn on profiling: ["@explain":true](http://apijson.cn:8080/get/{"[]":{"Moment":{"@explain":true}}})
⑩ Get the No.0 picture from pictureList: ["@position":0, //self-defined keyword "firstPicture()":"getFromArray(pictureList,@position)"](http://apijson.cn:8080/get/{"User":{"id":38710,"@position":0,"firstPicture()":"getFromArray(pictureList,@position)"}})
+ global keyword. | It is a keyword inside the outermost object {}. Among them, @database, @schema, @datasource, @role, and @explain are basically the same as object keywords, see the above description, the difference is that the global keywords will be automatically inserted in each table object as the default value.
① "tag": String, the following tag is the identifier of the JSON structure matching the request in non-GET or HEAD requests, generally it is the name of the Table to be queried or the array Table[] or Table:[] corresponding to the name, determined by the backend specified in the Request table.
② "version": Integer, the interface version. If the version is not passed, null or <=0, the highest version will be used. If other valid values are passed, the lowest version closest to it will be used, which is specified in the backend Request table.
③ "format": Boolean, formatted to return the key of the Response JSON, generally converting TableName to tableName, TableName[] to tableNameList, Table:alias to alias, TableName-key[] to tableNameKeyList and other camelcase formats. | ① Check private information:: [{"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})
② Use the version 1 interface to check private information:: [{"version":1,"tag":"Privacy","Privacy":{"id":82001}}](http://apijson.cn/api?url=http%3A%2F%2Fapijson.cn%3A8080%2Fgets&type=JSON&json={%22version%22:1,%22tag%22:%22Privacy%22,%22Privacy%22:{%22id%22:82001}})
③ Format Moments interface to return in JSON key: [{ "format":true, "[]":{ "page":0, "count":3, "Moment":{}, "User":{ "id@":"/Moment/userId" }, "Comment[]":{ "count":3, "Comment":{ "momentId@":"[]/Moment/id" } } } }](http://apijson.cn:8080/get/{"format":true,"[]":{"page":0,"count":3,"Moment":{},"User":{"id@":"%252FMoment%252FuserId"},"Comment[]":{"count":3,"Comment":{"momentId@":"[]%252FMoment%252Fid"}}}})
+
+
From fc9dc10c5e985ae480bf01d036b16f6a43c6abd4 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:26:30 +0800
Subject: [PATCH 44/98] add shortcuts for supported databases, languages,
frameworks in README.md
---
README.md | 40 +++++++++++++++++++++++++---------------
1 file changed, 25 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index ccafb985..239f4dc9 100644
--- a/README.md
+++ b/README.md
@@ -9,35 +9,42 @@ This source code is licensed under the Apache License Version 2.0
🏆 Tencent Top 6 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
- English
- 通用文档
+ English
+ 通用文档视频教程测试用例AI 问答
From fd32a8dd15d79910c8b916d92348b5bf9eff48e8 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:29:27 +0800
Subject: [PATCH 46/98] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 239f4dc9..20452f98 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-
🏆 Tencent Top 6 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
+
🏆 Tencent Top 5 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
From 50b60483648d9bc4890b39f610a8e20e39eff7ed Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:30:44 +0800
Subject: [PATCH 47/98] Update README-Chinese.md
---
README-Chinese.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README-Chinese.md b/README-Chinese.md
index 7f1a214c..95ad64c2 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -9,7 +9,7 @@ This source code is licensed under the Apache License Version 2.0
- English
+ English通用文档视频教程测试用例
From d25088727b829d6aaa7e79c1245ed693c40e7ca5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:33:30 +0800
Subject: [PATCH 48/98] Update README.md
---
README.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/README.md b/README.md
index 20452f98..9cbc1ab7 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,14 @@ This source code is licensed under the Apache License Version 2.0
🏆 Tencent Top 5 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
From 59491fe28b4f0ab3f64962a43f08045a5dace96e Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:34:15 +0800
Subject: [PATCH 49/98] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 9cbc1ab7..3953e029 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@ This source code is licensed under the Apache License Version 2.0
🏆 Tencent Top 5 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
- 中文版
- Document
+ 中文版
+ Document Video Test Ask AI
From 2f3e7bd0610ef239705b6b74fa96e6f18f2a3d69 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 16:37:30 +0800
Subject: [PATCH 50/98] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3953e029..3b28a95c 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-
🏆 Tencent Top 5 Open Source Project, Achieved 5 Awards Inside & Outside Tencent 🚀 A JSON Transmission Protocol and an ORM Library for providing APIs and Documents without writing any code.
+
🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and the response JSON can be customized by Frontend(Client) users
中文版
From 5c4b2a29afff336ff9fd7bea6b4c256e443e1208 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 17:25:13 +0800
Subject: [PATCH 51/98] Update README.md
---
README.md | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 3b28a95c..1104465d 100644
--- a/README.md
+++ b/README.md
@@ -84,8 +84,6 @@ This source code is licensed under the Apache License Version 2.0
---
-#### A better online document is available at https://apijsondocs.readthedocs.io/
-
* ### [1.About](#1)
* ### [2.Backend usage](#2)
* ### [3.Frontend usage](#3)
@@ -228,8 +226,8 @@ See the latest release [here](https://github.com/Tencent/APIJSON/releases/tag/5.
##
6. Author
-Check out the author's [github account](https://github.com/TommyLemon) to see more related projects.
-
+https://github.com/TommyLemon
+
If you have any questions or suggestions, you can [create an issue](https://github.com/Tencent/APIJSON/issues) or [send me an e-mail](mailto:tommylemon@qq.com).
From 30b5a6868a3e7d5cfc0210cddbb397ed92e491a5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 17:25:16 +0800
Subject: [PATCH 52/98] Update Document.md
---
Document.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Document.md b/Document.md
index 253fb6cb..7574e569 100644
--- a/Document.md
+++ b/Document.md
@@ -1,3 +1,5 @@
+#### A better online document is available at https://apijsondocs.readthedocs.io
+
### Examples:
#### Get a User
From 45582e71dc61b59752a61aef9496e992945dc359 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 17:26:30 +0800
Subject: [PATCH 53/98] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1104465d..822dc0f8 100644
--- a/README.md
+++ b/README.md
@@ -220,7 +220,7 @@ Please also ⭐Star the project!
##
5. Releases
-See the latest release [here](https://github.com/Tencent/APIJSON/releases/tag/5.2.0).
+See the latest release [here](https://github.com/Tencent/APIJSON/releases)
From fc255264c8acdf28a37e1645c1930d54eecc5a61 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 15 Feb 2026 17:30:15 +0800
Subject: [PATCH 54/98] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 822dc0f8..84bf92cc 100644
--- a/README.md
+++ b/README.md
@@ -89,7 +89,7 @@ This source code is licensed under the Apache License Version 2.0
* ### [3.Frontend usage](#3)
* ### [4.Contributing](#4)
* ### [5.Releases](#5)
-* ### [6.Author](#6)
+* ### [6.Creator](#6)
* ### [7.Donating](#7)
@@ -224,7 +224,7 @@ See the latest release [here](https://github.com/Tencent/APIJSON/releases)
-##
6. Author
+##
6. Creator
https://github.com/TommyLemon
From 92cbf66b6da68a10664e87711485632b052f2d7b Mon Sep 17 00:00:00 2001
From: hobostay <110hqc@gmail.com>
Date: Tue, 17 Feb 2026 19:39:10 +0800
Subject: [PATCH 55/98] fix(logging): Replace printStackTrace() with proper
logging
Replace all e.printStackTrace() calls with Log.e() to follow best
practices for error handling and logging. This provides:
- Consistent logging format with timestamps
- Better error tracking in production
- Ability to control logging via Log.DEBUG flag
- More meaningful error messages with context
Changes:
- AbstractSQLExecutor.java: 9 replacements
- AbstractParser.java: 6 replacements
- JSONRequest.java: 1 replacement
- JSR223ScriptExecutor.java: 1 replacement
Co-Authored-By: Claude Sonnet 4.5
---
.../main/java/apijson/orm/AbstractParser.java | 16 +++++++---------
.../java/apijson/orm/AbstractSQLExecutor.java | 18 +++++++++---------
.../src/main/java/apijson/orm/JSONRequest.java | 2 +-
.../orm/script/JSR223ScriptExecutor.java | 3 ++-
4 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index cd0b2c7b..4e8562ea 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -590,7 +590,7 @@ public M parseResponse(M request) {
onCommit();
}
catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "onObjectParse failed", e);
error = e;
onRollback();
@@ -1022,9 +1022,7 @@ public M newErrorResult(Exception e) {
*/
public M newErrorResult(Exception e, boolean isRoot) {
if (e != null) {
- // if (Log.DEBUG) {
- e.printStackTrace();
- // }
+ Log.e(TAG, "newErrorResult", e);
String msg = CommonException.getMsg(e);
int code = CommonException.getCode(e);
@@ -1921,7 +1919,7 @@ public static V getValue(Object parent, String[] pathKeys) {
v = getFromObjOrArr(v, k);
} catch (Throwable e) {
if (IS_PRINT_BIG_LOG) {
- e.printStackTrace();
+ Log.e(TAG, "getFromObjOrArr failed", e);
}
v = null;
}
@@ -2230,7 +2228,7 @@ && getSQLExecutor().getTransactionIsolation() == Connection.TRANSACTION_NONE) {
commit();
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "onCommit failed", e);
}
}
/**回滚事务
@@ -2245,12 +2243,12 @@ protected void onRollback() {
rollback();
}
catch (SQLException e1) {
- e1.printStackTrace();
+ Log.e(TAG, "onRollback failed", e1);
try {
rollback(null);
}
catch (SQLException e2) {
- e2.printStackTrace();
+ Log.e(TAG, "onRollback with null failed", e2);
}
}
}
@@ -2517,7 +2515,7 @@ protected M batchVerify(RequestMethod method, String tag, int version, String na
correctRequest.put(key, obj);
}
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "parseCorrectRequest failed", e);
throw new Exception(e); // 包装一层只是为了打印日志?看起来没必要
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 797ac3de..8a2862eb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -676,7 +676,7 @@ else if (hasPK) {
rs.close();
}
catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "close ResultSet failed", e);
}
}
}
@@ -947,7 +947,7 @@ protected void executeAppJoin(SQLConfig config, List resultList, Map
rs.close();
}
catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "close ResultSet failed in executeAppJoin", e);
}
}
}
@@ -1125,7 +1125,7 @@ else if (value instanceof Clob) { //SQL Server TEXT 类型 居然走这个
br.close();
}
catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "close BufferedReader failed", e);
}
}
@@ -1194,7 +1194,7 @@ public boolean isJSONType(@NotNull SQLConfig config, ResultSetMetaData
return true;
}
} catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "isJsonColumn failed", e);
}
// List json = config.getJson();
// return json != null && json.contains(label);
@@ -1357,7 +1357,7 @@ public void begin(int transactionIsolation) throws SQLException {
}
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "setAutoCommit failed in rollback", e);
}
}
}
@@ -1384,7 +1384,7 @@ public void rollback() throws SQLException {
}
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "rollback failed", e);
}
}
}
@@ -1416,7 +1416,7 @@ public void rollback(Savepoint savepoint) throws SQLException {
}
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "rollback with savepoint failed", e);
}
}
}
@@ -1442,7 +1442,7 @@ public void commit() throws SQLException {
}
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "commit failed", e);
}
}
}
@@ -1473,7 +1473,7 @@ public void close() {
}
}
catch (SQLException e) {
- e.printStackTrace();
+ Log.e(TAG, "close connection failed", e);
}
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
index 40bfa147..1670bf1f 100755
--- a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
@@ -99,7 +99,7 @@ public Object put(String key, Object value) {
target = JSON.parse(value);
} catch (Exception e) {
// nothing
- e.printStackTrace();
+ Log.e(TAG, "JSON.parse failed for key: " + key, e);
}
// if (target == null) { // "tag":"User" 报错
// return null;
diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
index 6a3a6c4f..45a1d129 100644
--- a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
@@ -12,6 +12,7 @@
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;
+import apijson.Log;
import apijson.orm.AbstractFunctionParser;
/**
@@ -45,7 +46,7 @@ public void load(String name, String script) {
CompiledScript compiledScript = ((Compilable) scriptEngine).compile(convertScript(script));
compiledScriptMap.put(name, compiledScript);
} catch (Exception e) {
- e.printStackTrace();
+ Log.e(TAG, "compile script failed: " + name, e);
}
}
From f92687907e03943a79dacfde2a25564b1834573b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Wed, 18 Feb 2026 20:23:39 +0800
Subject: [PATCH 56/98] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?=
=?UTF-8?q?=E7=B1=BB=EF=BC=8C=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E6=89=93?=
=?UTF-8?q?=E5=8D=B0=20Throwable=20=E5=92=8C=E9=BB=98=E8=AE=A4=E7=BA=A7?=
=?UTF-8?q?=E5=88=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/src/main/java/apijson/Log.java | 55 ++++++++++++++++-------
1 file changed, 39 insertions(+), 16 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 9cbacd0a..d301cdf0 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -7,14 +7,21 @@
import java.text.SimpleDateFormat;
-/**测试用Log
+/**测试用日志
* @modifier Lemon
*/
public class Log {
+ public static boolean DEBUG = false;
- public static boolean DEBUG = true;
+ public static final String LEVEL_VERBOSE = "VERBOSE";
+ public static final String LEVEL_INFO = "INFO";
+ public static final String LEVEL_DEBUG = "DEBUG";
+ public static final String LEVEL_WARN = "WARN";
+ public static final String LEVEL_ERROR = "ERROR";
- public static final String VERSION = "8.1.3";
+ public static String LEVEL = LEVEL_WARN;
+
+ public static final String VERSION = "8.1.5";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
@@ -46,23 +53,26 @@ public static void setDateFormat(String dateFormatString) {
* @param msg
* @param level
*/
- public static void logInfo(String TAG, String msg, String level){
- if(level.equals("DEBUG") || level .equals("ERROR") ||level.equals("WARN")){
- System.err.println(DATE_FORMAT.format(System.currentTimeMillis()) + ": " + TAG + "." + level + ": " + msg);
+ public static void logInfo(String TAG, String msg, String level) {
+ if (level == null || level.isEmpty()) {
+ level = LEVEL;
}
- else if(level.equals("VERBOSE") || level .equals("INFO") ){
+
+ if (level.equals(LEVEL_VERBOSE) || level.equals(LEVEL_INFO)) {
System.out.println(DATE_FORMAT.format(System.currentTimeMillis()) + ": " + TAG + "." + level + ": " + msg);
}
+ else if (level.equals(LEVEL_DEBUG) || level.equals(LEVEL_ERROR) || level.equals(LEVEL_WARN)) {
+ System.err.println(DATE_FORMAT.format(System.currentTimeMillis()) + ": " + TAG + "." + level + ": " + msg);
+ }
}
-
/**
* @param TAG
* @param msg
*/
public static void d(String TAG, String msg) {
if (DEBUG) {
- logInfo(TAG,msg,"DEBUG");
+ logInfo(TAG, msg, LEVEL_DEBUG);
}
}
@@ -72,7 +82,7 @@ public static void d(String TAG, String msg) {
* @param msg debug messages
*/
public static void fd(String TAG, String msg) {
- logInfo(TAG,msg,"DEBUG");
+ logInfo(TAG, msg, LEVEL_DEBUG);
}
/**
@@ -81,8 +91,8 @@ public static void fd(String TAG, String msg) {
* @param symbol used for generating separation line
* @param post postfix
*/
- public static void sl(String pre,char symbol ,String post) {
- System.err.println(pre+new String(new char[48]).replace('\u0000', symbol)+post);
+ public static void sl(String pre, char symbol, String post) {
+ System.err.println(pre + new String(new char[48]).replace('\u0000', symbol) + post);
}
/**
@@ -91,7 +101,7 @@ public static void sl(String pre,char symbol ,String post) {
*/
public static void v(String TAG, String msg) {
if (DEBUG) {
- logInfo(TAG,msg,"VERBOSE");
+ logInfo(TAG, msg, LEVEL_VERBOSE);
}
}
@@ -101,7 +111,7 @@ public static void v(String TAG, String msg) {
*/
public static void i(String TAG, String msg) {
if (DEBUG) {
- logInfo(TAG,msg,"INFO");
+ logInfo(TAG, msg, LEVEL_INFO);
}
}
@@ -111,7 +121,20 @@ public static void i(String TAG, String msg) {
*/
public static void e(String TAG, String msg) {
if (DEBUG) {
- logInfo(TAG,msg,"ERROR");
+ logInfo(TAG, msg, LEVEL_ERROR);
+ }
+ }
+
+ /**
+ * @param TAG
+ * @param msg
+ */
+ public static void e(String TAG, String msg, Throwable e) {
+ if (DEBUG) {
+ if (e != null) {
+ e.printStackTrace();
+ }
+ logInfo(TAG, msg, LEVEL_ERROR);
}
}
@@ -121,7 +144,7 @@ public static void e(String TAG, String msg) {
*/
public static void w(String TAG, String msg) {
if (DEBUG) {
- logInfo(TAG,msg,"WARN");
+ logInfo(TAG, msg, LEVEL_WARN);
}
}
From b3218da66cd55d9fec22683e877f65ce9bc8f9b7 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 00:59:08 +0800
Subject: [PATCH 57/98] fix: import Log, String TAG
---
APIJSONORM/src/main/java/apijson/orm/JSONRequest.java | 1 +
.../src/main/java/apijson/orm/script/JSR223ScriptExecutor.java | 2 ++
2 files changed, 3 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
index 1670bf1f..5ac92429 100755
--- a/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
+++ b/APIJSONORM/src/main/java/apijson/orm/JSONRequest.java
@@ -8,6 +8,7 @@
import java.util.*;
import apijson.JSON;
+import apijson.Log;
import apijson.StringUtil;
/**JSONRequest for Server to replace apijson.JSONMap,
diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
index 45a1d129..9c2c9baf 100644
--- a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
@@ -19,6 +19,8 @@
* JSR223 script engine的统一实现抽象类
*/
public abstract class JSR223ScriptExecutor, L extends List> implements ScriptExecutor {
+ private static final String TAG = "JSR223ScriptExecutor";
+
protected ScriptEngine scriptEngine;
private final Map compiledScriptMap = new ConcurrentHashMap<>();
From 5744eabecf1cde06776c33333a4b8d14e7979c52 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 02:37:45 +0800
Subject: [PATCH 58/98] verify: change "content{L}": ">0" to "content[{}": ">0"
---
.../main/java/apijson/orm/AbstractParser.java | 2 +-
.../java/apijson/orm/AbstractVerifier.java | 89 ++++++++++---------
.../src/main/java/apijson/orm/Operation.java | 5 +-
3 files changed, 51 insertions(+), 45 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index 4e8562ea..e2403122 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -2516,7 +2516,7 @@ protected M batchVerify(RequestMethod method, String tag, int version, String na
}
} catch (Exception e) {
Log.e(TAG, "parseCorrectRequest failed", e);
- throw new Exception(e); // 包装一层只是为了打印日志?看起来没必要
+ throw e;
}
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 55eeff40..1612cc3c 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -1331,7 +1331,7 @@ public static ScriptEngine getScriptEngine(String lang) {
* @return
* @throws Exception
*/
- private static , L extends List> M operate(Operation opt, M targetChild
+ public static , L extends List> M operate(Operation opt, M targetChild
, M real, @NotNull Parser parser) throws Exception {
if (targetChild == null) {
return real;
@@ -1522,7 +1522,7 @@ public static void verifyType(@NotNull String tk, @NotNull String tv, Object rv,
* @param parser
* @throws Exception
*/
- private static , L extends List> void verifyValue(@NotNull String tk
+ public static , L extends List> void verifyValue(@NotNull String tk
, @NotNull Object tv, @NotNull M real, @NotNull Parser parser) throws Exception {
if (tv == null) {
throw new IllegalArgumentException("operate operate == VERIFY " + tk + ":" + tv + " , >> tv == null!!!");
@@ -1576,7 +1576,7 @@ else if (tk.endsWith("~")) { // 正则匹配
}
}
else if (tk.endsWith("{}")) { //rv符合tv条件或在tv内
- if (tv instanceof String) {//TODO >= 0, < 10
+ if (tv instanceof String) { //TODO >= 0, < 10
verifyCondition("{}", real, tk, tv, parser);
}
else if (tv instanceof List>) {
@@ -1595,26 +1595,6 @@ else if (tv instanceof List>) {
throw new UnsupportedDataTypeException("服务器Request表verify配置错误!");
}
}
- else if (tk.endsWith("{L}")) { //字符串长度
- if (tv instanceof String) {
- logic = new Logic(tk.substring(0, tk.length() - 3));
-
- rk = logic.getKey();
- rv = real.get(rk);
- if (rv == null) {
- return;
- }
- String[] tvs = tv.toString().split(",");
- for (String tvItem : tvs) {
- if (!verifyRV(tvItem,rv.toString())) {
- throw new IllegalArgumentException(rk + ":value 中value长度不合法!必须匹配 " + tk + ":" + tv + " !");
- }
- }
- }
- else {
- throw new UnsupportedDataTypeException("服务器Request表verify配置错误!");
- }
- }
else if (tk.endsWith("<>")) { //rv包含tv内的值
logic = new Logic(tk.substring(0, tk.length() - 2));
rk = logic.getKey();
@@ -1655,41 +1635,39 @@ else if (tk.endsWith("<>")) { //rv包含tv内的值
}
}
- /**
- * 校验字符串长度
- *
+ /**校验字符串长度
* @param rule 规则
- * @param content 内容
+ * @param len 长度
* @return
* @throws UnsupportedDataTypeException
*/
- private static boolean verifyRV(String rule,String content) throws UnsupportedDataTypeException {
+ public static boolean verifyLength(String rule, int len) throws UnsupportedDataTypeException {
String first = null;
String second = null;
Matcher matcher = VERIFY_LENGTH_PATTERN.matcher(rule);
while (matcher.find()) {
- first = StringUtil.isEmpty(first)?matcher.group("first"):first;
- second = StringUtil.isEmpty(second)?matcher.group("second"):second;
+ first = StringUtil.isEmpty(first) ? matcher.group("first") : first;
+ second = StringUtil.isEmpty(second) ? matcher.group("second") : second;
}
// first和second为空表示规则不合法
- if(StringUtil.isEmpty(first) || StringUtil.isEmpty(second)){
+ if (StringUtil.isEmpty(first) || StringUtil.isEmpty(second)) {
throw new UnsupportedDataTypeException("服务器Request表verify配置错误!");
}
int secondNum = Integer.parseInt(second);
- switch (Objects.requireNonNull(first)){
+ switch (Objects.requireNonNull(first)) {
case ">":
- return content.length() > secondNum;
+ return len > secondNum;
case ">=":
- return content.length() >= secondNum;
+ return len >= secondNum;
case "<":
- return content.length() < secondNum;
+ return len < secondNum;
case "<=":
- return content.length() <= secondNum;
+ return len <= secondNum;
case "<>":
- return content.length() != secondNum;
- default:
+ return len != secondNum;
}
+
// 出现不能识别的符号也认为规则不合法
throw new UnsupportedDataTypeException("服务器Request表verify配置错误!");
}
@@ -1702,13 +1680,40 @@ private static boolean verifyRV(String rule,String content) throws UnsupportedDa
* @param parser
* @throws Exception
*/
- private static , L extends List> void verifyCondition(
+ public static , L extends List> void verifyCondition(
@NotNull String funChar, @NotNull M real, @NotNull String tk, @NotNull Object tv
, @NotNull Parser parser) throws Exception {
- //不能用Parser, 0 这种不符合 StringUtil.isName !
- Logic logic = new Logic(tk.substring(0, tk.length() - funChar.length()));
+ // 不能用Parser, 0 这种不符合 StringUtil.isName !
+
+ boolean isRange = "{}".equals(funChar);
+
+ String k = tk.substring(0, tk.length() - funChar.length());
+ Logic logic = new Logic(k);
String rk = logic.getKey();
- Object rv = real.get(rk);
+ boolean isLen = isRange && k.endsWith("[");
+ boolean isJSOnLen = isRange && k.endsWith("{");
+ k = isLen || isJSOnLen ? rk.substring(0, rk.length() - 1) : rk;
+ Object rv = real.get(k);
+ int len = 0;
+ if (isLen) {
+ len = StringUtil.length(rv, false);
+ }
+ else if (isJSOnLen) {
+ rv = rv instanceof Map ? ((Map, ?>) rv) : (rv instanceof Collection ? ((Collection>) rv) : JSON.parse(rv));
+ len = rv instanceof Map ? ((Map, ?>) rv).size() : (rv instanceof Collection ? ((Collection>) rv).size() : StringUtil.length(rv, false));
+ }
+
+ if (isLen || isJSOnLen) {
+ String[] tvs = tv.toString().split(",");
+ for (String tvItem : tvs) {
+ if (! verifyLength(tvItem, len)) {
+ throw new IllegalArgumentException(k + ":value 中value长度不合法!必须匹配 " + tk + ":" + tv + " !");
+ }
+ }
+
+ return;
+ }
+
if (rv == null) {
return;
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java
index 1c4f2dc5..b69eb9a7 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Operation.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java
@@ -41,7 +41,7 @@ public enum Operation {
* @see {@link AbstractVerifier#verifyType(String, String, Object, boolean)}
*/
TYPE,
-
+
/**
* 验证是否符合预设的条件,结构是
* {
@@ -54,7 +54,8 @@ public enum Operation {
* {
* "phone~": "PHONE", //phone 必须满足 PHONE 的格式,配置见 {@link AbstractVerifier#COMPILE_MAP}
* "status{}": [1,2,3], //status 必须在给出的范围内
- * "content{L}": ">0,<=255", //content的长度 必须在给出的范围内
+ * "content[{}": ">0", //content的长度 必须在给出的范围内
+ * "pictureList{&{}": ">0,<=10", //pictureList 的 JSON 长度必须在给出的范围内
* "balance&{}":">0,<=10000" //必须满足 balance>0 & balance<=10000
* }
*/
From bccb5ef51a2f114f285c767d458f758c334c6b6b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 02:45:23 +0800
Subject: [PATCH 59/98] feat: add @trim: "name,tag" to trim string values for
keys
---
APIJSONORM/src/main/java/apijson/JSONMap.java | 10 ++
.../apijson/orm/AbstractObjectParser.java | 24 +++-
.../java/apijson/orm/AbstractVerifier.java | 111 +++++++++---------
3 files changed, 82 insertions(+), 63 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/JSONMap.java b/APIJSONORM/src/main/java/apijson/JSONMap.java
index 3b93d69d..c2913055 100755
--- a/APIJSONORM/src/main/java/apijson/JSONMap.java
+++ b/APIJSONORM/src/main/java/apijson/JSONMap.java
@@ -194,6 +194,7 @@ default JSONMap setUserIdIn(List list) {
String KEY_RAW = "@raw"; // 自定义原始 SQL 片段
String KEY_JSON = "@json"; // 把字段转为 JSON 输出
String KEY_STRING = "@string"; // 把字段转为 String 输入
+ String KEY_TRIM = "@trim"; // 去除首位空格等空白字符
String KEY_METHOD = "@method"; // json 对象配置操作方法
String KEY_GET = "@get"; // json 对象配置操作方法
String KEY_GETS = "@gets"; // json 对象配置操作方法
@@ -229,6 +230,7 @@ default JSONMap setUserIdIn(List list) {
KEY_RAW,
KEY_JSON,
KEY_STRING,
+ KEY_TRIM,
KEY_METHOD,
KEY_GET,
KEY_GETS,
@@ -548,6 +550,14 @@ default JSONMap setString(String keys) {
return puts(KEY_STRING, keys);
}
+ /**set keys to cast to string
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ default JSONMap setTrim(String keys) {
+ return puts(KEY_TRIM, keys);
+ }
+
//JSONObject内关键词 key >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 0e6aaa78..c49c9cd2 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -62,6 +62,7 @@ public AbstractObjectParser setParser(Parser parser) {
*/
protected final boolean drop;
private List stringKeyList;
+ private List trimKeyList;
/**for single object
*/
@@ -109,6 +110,11 @@ public AbstractObjectParser(@NotNull M request, String parentPath, SQLConfig parse(String name, boolean isReuse) throws
// hasOtherKeyNotFun = true;
// }
- if (stringKeyList != null && stringKeyList.contains(key)) {
- // 统一格式 String val = value == null || value instanceof String ? (String) value : JSON.toJSONString(value);
- if (onParse(key, JSON.toJSONString(value)) == false) {
- invalidate();
- }
+ boolean isTrim = (trimKeyList != null && trimKeyList.contains(key));
+ boolean toStr = isTrim || (stringKeyList != null && stringKeyList.contains(key));
+ if (toStr) {
+ value = JSON.toJSONString(value);
}
- else if (startsWithAt || key.endsWith("@") || (key.endsWith("<>") && value instanceof Map, ?>)) {
+ if (isTrim && value != null) {
+ value = StringUtil.trim(value);
+ }
+
+ if (startsWithAt || key.endsWith("@") || (key.endsWith("<>") && value instanceof Map, ?>)) {
if (onParse(key, value) == false) {
invalidate();
}
@@ -1242,6 +1251,9 @@ public void recycle() {
if (stringKeyList != null) { // 避免被全局关键词覆盖 && ! stringKeyList.isEmpty()) {
request.put(KEY_STRING, StringUtil.get(stringKeyList.toArray()));
}
+ if (trimKeyList != null) { // 避免被全局关键词覆盖 && ! trimKeyList.isEmpty()) {
+ request.put(KEY_TRIM, StringUtil.get(trimKeyList.toArray()));
+ }
method = null;
parentPath = null;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 1612cc3c..5891114a 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -14,18 +14,7 @@
import static apijson.RequestMethod.HEADS;
import static apijson.RequestMethod.POST;
import static apijson.RequestMethod.PUT;
-import static apijson.orm.Operation.ALLOW_PARTIAL_UPDATE_FAIL;
-import static apijson.orm.Operation.EXIST;
-import static apijson.orm.Operation.INSERT;
-import static apijson.orm.Operation.MUST;
-import static apijson.orm.Operation.REFUSE;
-import static apijson.orm.Operation.REMOVE;
-import static apijson.orm.Operation.REPLACE;
-import static apijson.orm.Operation.TYPE;
-import static apijson.orm.Operation.UNIQUE;
-import static apijson.orm.Operation.UPDATE;
-import static apijson.orm.Operation.VERIFY;
-import static apijson.orm.Operation.IF;
+import static apijson.orm.Operation.*;
//import static apijson.orm.Operation.CODE;
import java.net.URL;
@@ -225,11 +214,11 @@ public String getVisitorIdKey(SQLConfig config) {
@Override
public String getIdKey(String database, String schema, String datasource, String table) {
- return JSONMap.KEY_ID;
+ return KEY_ID;
}
@Override
public String getUserIdKey(String database, String schema, String datasource, String table) {
- return JSONMap.KEY_USER_ID;
+ return KEY_USER_ID;
}
@SuppressWarnings("unchecked")
@@ -384,13 +373,13 @@ public void verifyUseRole(@NotNull SQLConfig config, String table, Requ
Collection requestIdArray = (Collection) config.getWhere(visitorIdKey + "{}", true); // 不能是 &{}, |{} 不要传,直接 {}
if (requestId != null) {
if (requestIdArray == null) {
- requestIdArray = JSON.createJSONArray();
+ requestIdArray = createJSONArray();
}
requestIdArray.add(requestId);
}
if (requestIdArray == null) { // 可能是 @ 得到 || requestIdArray.isEmpty()) { // 请求未声明 key:id 或 key{}:[...] 条件,自动补全
- config.putWhere(visitorIdKey+"{}", JSON.parseArray(list), true); // key{}:[] 有效,SQLConfig 里 throw NotExistException
+ config.putWhere(visitorIdKey+"{}", parseArray(list), true); // key{}:[] 有效,SQLConfig 里 throw NotExistException
}
else { // 请求已声明 key:id 或 key{}:[] 条件,直接验证
for (Object id : requestIdArray) {
@@ -417,7 +406,7 @@ else if (id instanceof String) {
}
break;
case OWNER:
- if (config.getMethod() == RequestMethod.POST) {
+ if (config.getMethod() == POST) {
List c = config.getColumn();
List> ovs = config.getValues();
if ( (c == null || c.isEmpty()) || (ovs == null || ovs.isEmpty()) ) {
@@ -533,17 +522,17 @@ public void verifyRepeat(String table, String key, Object value, long exceptId)
throw new UnsupportedDataTypeException(key + ":value 中value的类型不能为JSON!");
}
- M tblObj = JSON.createJSONObject();
+ M tblObj = createJSONObject();
tblObj.put(key, value);
if (exceptId > 0) {//允许修改自己的属性为该属性原来的值
- tblObj.put(JSONMap.KEY_ID + "!", exceptId); // FIXME 这里 id 写死了,不支持自定义
+ tblObj.put(KEY_ID + "!", exceptId); // FIXME 这里 id 写死了,不支持自定义
}
- M req = JSON.createJSONObject();
+ M req = createJSONObject();
req.put(table, tblObj);
Map repeat = createParser().setMethod(HEAD).setNeedVerify(true).parseResponse(req);
- repeat = repeat == null ? null : JSON.get(repeat, table);
+ repeat = repeat == null ? null : get(repeat, table);
if (repeat == null) {
throw new Exception("服务器内部错误 verifyRepeat repeat == null");
}
@@ -647,8 +636,8 @@ public static , L extends List> M verif
}
Log.i(TAG, "verifyRequest method = " + method + "; name = " + name
- + "; target = \n" + JSON.toJSONString(target)
- + "\n request = \n" + JSON.toJSONString(request));
+ + "; target = \n" + toJSONString(target)
+ + "\n request = \n" + toJSONString(request));
if (target == null || request == null) {// || request.isEmpty()) {
Log.i(TAG, "verifyRequest target == null || request == null >> return null;");
@@ -673,10 +662,10 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
if (tobj != null) {//不允许不传Target中指定的Table
throw new IllegalArgumentException(method + "请求,请在 " + name + " 内传 " + key + ":{} !");
}
- } else if (JSONMap.isTableKey(key)) {
- String db = getString(request, JSONMap.KEY_DATABASE);
- String sh = getString(request, JSONMap.KEY_SCHEMA);
- String ds = getString(request, JSONMap.KEY_DATASOURCE);
+ } else if (isTableKey(key)) {
+ String db = getString(request, KEY_DATABASE);
+ String sh = getString(request, KEY_SCHEMA);
+ String ds = getString(request, KEY_DATASOURCE);
if (StringUtil.isEmpty(db, false)) {
db = database;
}
@@ -688,9 +677,9 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
}
String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, key);
- String finalIdKey = StringUtil.isEmpty(idKey, false) ? JSONMap.KEY_ID : idKey;
+ String finalIdKey = StringUtil.isEmpty(idKey, false) ? KEY_ID : idKey;
- if (method == RequestMethod.POST) {
+ if (method == POST) {
if (robj.containsKey(finalIdKey)) {
throw new IllegalArgumentException(method + "请求," + name + "/" + key + " 不能传 " + finalIdKey + " !");
}
@@ -700,7 +689,7 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
verifyId(method.name(), name, key, robj, finalIdKey, maxUpdateCount, atLeastOne != null ? atLeastOne : IS_UPDATE_MUST_HAVE_ID_CONDITION);
String userIdKey = idCallback == null ? null : idCallback.getUserIdKey(db, sh, ds, key);
- String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? JSONMap.KEY_USER_ID : userIdKey;
+ String finalUserIdKey = StringUtil.isEmpty(userIdKey, false) ? KEY_USER_ID : userIdKey;
verifyId(method.name(), name, key, robj, finalUserIdKey, maxUpdateCount, false);
}
}
@@ -711,7 +700,7 @@ public M onParseJSONObject(String key, M tobj, M robj) throws Exception {
@Override
protected L onParseJSONArray(String key, L tarray, L rarray) throws Exception {
- if ((method == RequestMethod.POST || method == RequestMethod.PUT) && JSONMap.isArrayKey(key)) {
+ if ((method == POST || method == PUT) && isArrayKey(key)) {
if (rarray == null || rarray.isEmpty()) {
throw new IllegalArgumentException(method + "请求,请在 " + name + " 内传 " + key + ":[{ ... }] "
+ ",批量新增 Table[]:value 中 value 必须是包含表对象的非空数组!其中每个子项 { ... } 都是"
@@ -753,7 +742,7 @@ private static , L extends List> void v
String idRefInKey = getString(robj, idKey + "{}@");
L idIn = null;
try {
- idIn = JSON.get(robj, idInKey); //如果必须传 id{} ,可在Request表中配置NECESSARY
+ idIn = get(robj, idInKey); //如果必须传 id{} ,可在Request表中配置NECESSARY
} catch (Exception e) {
throw new IllegalArgumentException(method + "请求," + name + "/" + key
+ " 里面的 " + idInKey + ":value 中value的类型只能是 [Long] !");
@@ -850,8 +839,8 @@ public static , L extends List> M veri
, final IdCallback idKeyCallback, @NotNull Parser parser, OnParseCallback callback) throws Exception {
Log.i(TAG, "verifyResponse method = " + method + "; name = " + name
- + "; target = \n" + JSON.toJSONString(target)
- + "\n response = \n" + JSON.toJSONString(response));
+ + "; target = \n" + toJSONString(target)
+ + "\n response = \n" + toJSONString(response));
if (target == null || response == null) {// || target.isEmpty() {
Log.i(TAG, "verifyResponse target == null || response == null >> return response;");
@@ -933,11 +922,11 @@ public static , L extends List> M parse
}
// 获取配置<<<<<<<<<<<<<<<<<<<<<<<<<<<<
- M type = JSON.get(target, TYPE.name());
- M verify = JSON.get(target, VERIFY.name());
- M insert = JSON.get(target, INSERT.name());
- M update = JSON.get(target, UPDATE.name());
- M replace = JSON.get(target, REPLACE.name());
+ M type = get(target, TYPE.name());
+ M verify = get(target, VERIFY.name());
+ M insert = get(target, INSERT.name());
+ M update = get(target, UPDATE.name());
+ M replace = get(target, REPLACE.name());
String exist = StringUtil.get(getString(target, EXIST.name()));
String unique = StringUtil.get(getString(target, UNIQUE.name()));
@@ -973,7 +962,9 @@ public static , L extends List> M parse
// 判断必要字段是否都有>>>>>>>>>>>>>>>>>>>
String[] sks = StringUtil.split(getString(real, KEY_STRING));
+ String[] trims = StringUtil.split(getString(real, KEY_TRIM));
List stringKeyList = sks == null || sks.length <= 0 ? null : Arrays.asList(sks);
+ List trimKeyList = trims == null || trims.length <= 0 ? null : Arrays.asList(trims);
Set objKeySet = new HashSet(); // 不能用tableKeySet,仅判断 Table:{} 会导致 key:{ Table:{} } 绕过判断
@@ -990,7 +981,10 @@ public static , L extends List> M parse
Object tvalue = entry.getValue();
Object rvalue = real.get(key);
if (rvalue != null && stringKeyList != null && stringKeyList.contains(key)) {
- rvalue = JSON.toJSONString(rvalue);
+ rvalue = toJSONString(rvalue);
+ }
+ if (rvalue != null && trimKeyList != null && trimKeyList.contains(key)) {
+ rvalue = StringUtil.trim(rvalue);
}
if (callback.onParse(key, tvalue, rvalue) == false) {
@@ -1010,7 +1004,7 @@ public static , L extends List> M parse
}
tvalue = callback.onParseJSONArray(key, (L) tvalue, (L) rvalue);
- if ((method == RequestMethod.POST || method == RequestMethod.PUT) && JSONMap.isArrayKey(key)) {
+ if ((method == POST || method == PUT) && isArrayKey(key)) {
objKeySet.add(key);
}
} else { // 其它Object
@@ -1093,7 +1087,7 @@ public static , L extends List> M parse
// 判断不允许传的key<<<<<<<<<<<<<<<<<<<<<<<<<
for (String rk : rkset) {
- if (rk == null || KEY_STRING.equals(rk)) {
+ if (rk == null || KEY_STRING.equals(rk) || KEY_TRIM.equals(rk)) {
// ConcurrentModificationException real.remove(rk);
continue;
}
@@ -1114,7 +1108,10 @@ public static , L extends List> M parse
Object rv = real.get(rk);
if (rv != null && stringKeyList != null && stringKeyList.contains(rk)) {
- rv = JSON.toJSONString(rv);
+ rv = toJSONString(rv);
+ }
+ if (rv != null && trimKeyList != null && trimKeyList.contains(rk)) {
+ rv = StringUtil.trim(rv);
}
// 不允许传远程函数,只能后端配置
@@ -1129,8 +1126,8 @@ public static , L extends List> M parse
throw new UnsupportedOperationException(method + " 请求,"
+ name + " 里面不允许传 " + rk + ":{} !");
}
- if ((method == RequestMethod.POST || method == RequestMethod.PUT)
- && rv instanceof List> && JSONMap.isArrayKey(rk)) {
+ if ((method == POST || method == PUT)
+ && rv instanceof List> && isArrayKey(rk)) {
throw new UnsupportedOperationException(method + " 请求," + name + " 里面不允许 "
+ rk + ":[] 等未定义的 Table[]:[{}] 批量操作键值对!");
}
@@ -1155,9 +1152,9 @@ public static , L extends List> M parse
// 校验与修改Request>>>>>>>>>>>>>>>>>
- String db = getString(real, JSONMap.KEY_DATABASE);
- String sh = getString(real, JSONMap.KEY_SCHEMA);
- String ds = getString(real, JSONMap.KEY_DATASOURCE);
+ String db = getString(real, KEY_DATABASE);
+ String sh = getString(real, KEY_SCHEMA);
+ String ds = getString(real, KEY_DATASOURCE);
if (StringUtil.isEmpty(db, false)) {
db = database;
}
@@ -1168,7 +1165,7 @@ public static , L extends List> M parse
ds = datasource;
}
String idKey = idCallback == null ? null : idCallback.getIdKey(db, sh, ds, name);
- String finalIdKey = StringUtil.isEmpty(idKey, false) ? JSONMap.KEY_ID : idKey;
+ String finalIdKey = StringUtil.isEmpty(idKey, false) ? KEY_ID : idKey;
// TODO 放在operate前?考虑性能、operate修改后再验证的值是否和原来一样
// 校验存在<<<<<<<<<<<<<<<<<<<
@@ -1201,7 +1198,7 @@ public static , L extends List> M parse
String[] partialFails = StringUtil.split(allowPartialUpdateFail);
if (partialFails != null && partialFails.length > 0) {
for (String key : partialFails) {
- if (JSONMap.isArrayKey(key) == false) {
+ if (isArrayKey(key) == false) {
throw new IllegalArgumentException("后端 Request 表中 " + ALLOW_PARTIAL_UPDATE_FAIL.name()
+ ":value 中 " + key + " 不合法!必须以 [] 结尾!");
}
@@ -1224,7 +1221,7 @@ public static , L extends List> M parse
// 校验并配置允许部分批量增删改失败>>>>>>>>>>>>>>>>>>>
- String[] nks = ifObj == null ? null : StringUtil.split(getString(real, JSONMap.KEY_NULL));
+ String[] nks = ifObj == null ? null : StringUtil.split(getString(real, KEY_NULL));
Collection> nkl = nks == null || nks.length <= 0 ? new HashSet<>() : Arrays.asList(nks);
Set> ifSet = ifObj == null ? null : ifObj.entrySet();
@@ -1234,7 +1231,7 @@ public static , L extends List> M parse
// , apijson.JSONMap.KEY_USER_ID, apijson.JSONMap.KEY_USER_ID_IN));
// condKeys.addAll(apijson.JSONMap.TABLE_KEY_LIST);
- String preCode = "var curObj = " + JSON.toJSONString(real) + ";";
+ String preCode = "var curObj = " + toJSONString(real) + ";";
// 未传的 key 在后面 eval 时总是报错 undefined,而且可能有冲突,例如对象里有 "curObj": val 键值对,就会覆盖当前对象定义,还不如都是 curObj.sex 这样取值
// Set> rset = real.entrySet();
@@ -1307,7 +1304,7 @@ public static , L extends List> M parse
}
}
- Log.i(TAG, "parse return real = " + JSON.toJSONString(real));
+ Log.i(TAG, "parse return real = " + toJSONString(real));
return real;
}
@@ -1722,7 +1719,7 @@ else if (isJSOnLen) {
throw new IllegalArgumentException(rk + ":value 中value不合法!value 中不允许有单引号 ' !");
}
- SQLConfig config = parser.createSQLConfig().setMethod(RequestMethod.GET).setCount(1).setPage(0);
+ SQLConfig config = parser.createSQLConfig().setMethod(GET).setCount(1).setPage(0);
config.setTest(true);
// config.setTable(Test.class.getSimpleName());
// config.setColumn(rv + logic.getChar() + funChar)
@@ -1777,7 +1774,7 @@ public static , L extends List> void ve
return;
}
- SQLConfig config = parser.createSQLConfig().setMethod(RequestMethod.HEAD).setCount(1).setPage(0);
+ SQLConfig config = parser.createSQLConfig().setMethod(HEAD).setCount(1).setPage(0);
config.setTable(table);
param.forEach((key,value) -> config.putWhere(key, value, false));
@@ -1860,9 +1857,9 @@ public static , L extends List> void ve
return;
}
- String finalIdKey = StringUtil.isEmpty(idKey, false) ? JSONMap.KEY_ID : idKey;
+ String finalIdKey = StringUtil.isEmpty(idKey, false) ? KEY_ID : idKey;
- SQLConfig config = parser.createSQLConfig().setMethod(RequestMethod.HEAD).setCount(1).setPage(0);
+ SQLConfig config = parser.createSQLConfig().setMethod(HEAD).setCount(1).setPage(0);
config.setTable(table);
if (exceptId > 0) { //允许修改自己的属性为该属性原来的值
config.putWhere(finalIdKey + "!", exceptId, false);
From 7b3bf77a6180fe80df736da142b25650ff0e558f Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 02:58:47 +0800
Subject: [PATCH 60/98] fix: add != and = for verifying request, remove <>
---
.../src/main/java/apijson/orm/AbstractVerifier.java | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 5891114a..5e665815 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -1653,16 +1653,18 @@ public static boolean verifyLength(String rule, int len) throws UnsupportedDataT
int secondNum = Integer.parseInt(second);
switch (Objects.requireNonNull(first)) {
- case ">":
- return len > secondNum;
case ">=":
return len >= secondNum;
- case "<":
- return len < secondNum;
case "<=":
return len <= secondNum;
- case "<>":
+ case "!=":
return len != secondNum;
+ case ">":
+ return len > secondNum;
+ case "<":
+ return len < secondNum;
+ case "=":
+ return len == secondNum;
}
// 出现不能识别的符号也认为规则不合法
From b38f506917162c80e37516d9f53451560fe1417b Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 04:03:39 +0800
Subject: [PATCH 61/98] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 84bf92cc..9f45d0b4 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-
🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and the response JSON can be customized by Frontend(Client) users
+
🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and Frontend can customize response JSONs
中文版
From 60e873d440bb11236820fdc3a7fcf697687c36f5 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 04:05:58 +0800
Subject: [PATCH 62/98] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 9f45d0b4..bd7b039d 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ This source code is licensed under the Apache License Version 2.0
-
🏆 Real-Time coding-free, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and Frontend can customize response JSONs
+
🏆 Real-Time no-code, powerful and secure ORM 🚀 providing APIs and Docs without coding by Backend, and Frontend can customize response JSONs
中文版
From e6840d00348488ea47ff7a60610b57a51d3c0a4a Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 22 Feb 2026 04:13:37 +0800
Subject: [PATCH 63/98] Authors of other projects for ecosystem of APIJSON(2
Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) expert ...
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index bd7b039d..b4a56e51 100644
--- a/README.md
+++ b/README.md
@@ -344,7 +344,7 @@ https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md
-Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) specialist, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
+Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) expert, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) expert, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
-
-
+
+
Authors of other projects for ecosystem of APIJSON(2 Tencent engineers, 1 BAT(Baidu/Alibaba/Tencent) expert, 1 Microsoft engineer, 2 Bytedance(TikTok) engineers, 1 Digital China engineer & Apache dubbo2js author, etc.):
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count
From f31e5858d57bbda980b5392cfb61530056d76e75 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 5 May 2026 21:44:58 +0800
Subject: [PATCH 81/98] =?UTF-8?q?=E7=94=9F=E6=80=81=E9=A1=B9=E7=9B=AE?=
=?UTF-8?q?=E6=96=B0=E5=A2=9E=20APIJSONServer=20-=20=E5=9F=BA=E4=BA=8EAPIJ?=
=?UTF-8?q?SON=E5=AE=9E=E7=8E=B0=E7=9A=84=E6=95=B0=E6=8D=AE=E6=9C=8D?=
=?UTF-8?q?=E5=8A=A1=E7=AB=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
感谢 @cyber2jie 的贡献,创作不易,点亮 ⭐️ Star 收藏/支持下吧 ^_^
https://github.com/cyber2jie/APIJSONServer
---
README-Chinese.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README-Chinese.md b/README-Chinese.md
index 3e9b446f..8fd9e98c 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -662,6 +662,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-spring-boot](https://gitee.com/yunjiao-source/apijson-spring-boot) Springboot3 for APIJSON,用 YAML 简化代码配置
+[APIJSONServer](https://github.com/cyber2jie/APIJSONServer) 基于APIJSON实现的数据服务端
+
感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
From c26c64c8ee3ca5c298e36286f745d94b5d2288de Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Mon, 11 May 2026 04:42:41 +0800
Subject: [PATCH 82/98] =?UTF-8?q?=E9=9D=9E=20DEBUG=20=E6=A8=A1=E5=BC=8F?=
=?UTF-8?q?=E4=B8=8D=E5=85=81=E8=AE=B8=E5=A4=96=E9=83=A8=E6=93=8D=E4=BD=9C?=
=?UTF-8?q?=20APIJSON=20=E9=85=8D=E7=BD=AE=E8=A1=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java | 6 +++++-
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 5f869837..fb050e32 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.TencentAPIJSON
- 8.1.6
+ 8.1.7jarAPIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index 5a4d2169..e4bd7ba7 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -21,7 +21,7 @@ public class Log {
public static String LEVEL = LEVEL_WARN;
- public static final String VERSION = "8.1.6";
+ public static final String VERSION = "8.1.7";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 9824d488..f0257030 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -108,7 +108,7 @@ public abstract class AbstractVerifier, L exten
// >
// >
@NotNull
- public static Map> SYSTEM_ACCESS_MAP;
+ public static Map> SYSTEM_ACCESS_MAP; // TODO 改名为 CONFIG_ACCESS_MAP ?
@NotNull
public static Map> ACCESS_MAP;
@NotNull
@@ -319,6 +319,10 @@ public void verifyAllowRole(SQLConfig config, String table, RequestMeth
role = config == null ? UNKNOWN : config.getRole();
}
+ if (Log.DEBUG == false && SYSTEM_ACCESS_MAP.get(table) != null) {
+ throw new IllegalAccessException(table + " 不允许 " + role + " 用户的 " + method.name() + " 请求!");
+ }
+
Map map = ACCESS_MAP.get(table);
if (map == null || Arrays.asList(map.get(method)).contains(role) == false) {
From 699f78d83196c4e49d14c456638a00ec210a4691 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sat, 16 May 2026 01:17:01 +0800
Subject: [PATCH 83/98] Update AbstractFunctionParser.java
---
.../src/main/java/apijson/orm/AbstractFunctionParser.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
index 42831775..ac0cc308 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
@@ -33,7 +33,7 @@ public abstract class AbstractFunctionParser, L
public static boolean ENABLE_REMOTE_FUNCTION = true;
/**开启支持远程函数中的 JavaScript 脚本形式
*/
- public static boolean ENABLE_SCRIPT_FUNCTION = true;
+ public static boolean ENABLE_SCRIPT_FUNCTION = false;
//
// >
@@ -961,4 +961,4 @@ public V getArgVal(String key, Class clazz, boolean defaultValue) throws
}
}
-}
\ No newline at end of file
+}
From b11b822808ab532116897616d7da13156626b74e Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 16 May 2026 22:16:02 +0800
Subject: [PATCH 84/98] Update AbstractVerifier.java
---
APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index f0257030..81ccc1ee 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -14,6 +14,7 @@
import static apijson.RequestMethod.HEADS;
import static apijson.RequestMethod.POST;
import static apijson.RequestMethod.PUT;
+import static apijson.orm.AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION;
import static apijson.orm.Operation.*;
//import static apijson.orm.Operation.CODE;
@@ -1339,6 +1340,11 @@ && rv instanceof List> && isArrayKey(rk)) {
}
public static ScriptEngine getScriptEngine(String lang) {
+ if (ENABLE_SCRIPT_FUNCTION == false) {
+ throw new UnsupportedOperationException("AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION" +
+ " == false 时不支持执行脚本!如需支持则设置为 true !");
+ }
+
boolean isEmpty = StringUtil.isEmpty(lang, true);
ScriptEngine engine = isEmpty ? SCRIPT_ENGINE : SCRIPT_ENGINE_MANAGER.getEngineByName(lang);
From a7d0f0f0d41ca2de4d7917f6c64a4ae2fd5b5f38 Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 16 May 2026 23:42:37 +0800
Subject: [PATCH 85/98] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=84=9A=E6=9C=AC?=
=?UTF-8?q?=E5=BC=95=E6=93=8E=E7=9A=84=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/main/java/apijson/orm/AbstractFunctionParser.java | 4 +++-
APIJSONORM/src/main/java/apijson/orm/Operation.java | 7 +++++++
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
index ac0cc308..7ed7fc4d 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractFunctionParser.java
@@ -31,7 +31,9 @@ public abstract class AbstractFunctionParser, L
/**开启支持远程函数
*/
public static boolean ENABLE_REMOTE_FUNCTION = true;
- /**开启支持远程函数中的 JavaScript 脚本形式
+ /**开启支持远程函数中的 JavaScript/Python/Lua/PHP 等脚本形式。
+ * JDK 8~13 可用自带 Nashorn 这个 js 引擎,注意配置 ClassFilter 防脚本注入攻击;
+ * 其它语言及 JDK 14+ 都必须依赖外部脚本引擎,注意按对应引擎说明方式防脚本注入攻击,最好是沙箱环境。
*/
public static boolean ENABLE_SCRIPT_FUNCTION = false;
diff --git a/APIJSONORM/src/main/java/apijson/orm/Operation.java b/APIJSONORM/src/main/java/apijson/orm/Operation.java
index b69eb9a7..45fcbf47 100755
--- a/APIJSONORM/src/main/java/apijson/orm/Operation.java
+++ b/APIJSONORM/src/main/java/apijson/orm/Operation.java
@@ -124,9 +124,16 @@ public enum Operation {
* 例如 "sex != 0 && sex != 1": "throw new Error('sex 必须在 [0, 1] 内!')"
* 自定义代码,当满足条件是执行后面的代码
*
+ * 还可以指定语言,例如 "python:sex not in(0, 1)": "throw new Error('sex 必须在 [0, 1] 内!')"
+ *
* 还有
* "ELSE": ""
* 自定义代码,不处理,和不传一样
+ *
+ * 需要
+ * 1.AbstractFunctionParser.ENABLE_SCRIPT_FUNCTION = true 启用
+ * 2.JDK 8~13 可用自带 Nashorn 这个 js 引擎,注意配置 ClassFilter 防脚本注入攻击;
+ * 其它语言及 JDK 14+ 都必须依赖外部脚本引擎,注意按对应引擎说明方式防脚本注入攻击,最好是沙箱环境。
*/
IF,
From 1344073f5e7c9c6f7590d18ca9b4c597f4db248a Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 16 May 2026 23:45:14 +0800
Subject: [PATCH 86/98] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E7=BD=AE?=
=?UTF-8?q?=E8=A1=A8=20model=20=E7=9A=84=E6=9D=83=E9=99=90=EF=BC=8C?=
=?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=86=8D=E9=9C=80=E8=A6=81=E7=9A=84?=
=?UTF-8?q?=20TestRecord?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../java/apijson/orm/AbstractSQLConfig.java | 2 -
.../java/apijson/orm/AbstractVerifier.java | 2 -
.../java/apijson/orm/ConfigMethodAccess.java | 29 +++++
.../main/java/apijson/orm/model/Access.java | 6 +-
.../java/apijson/orm/model/AllColumn.java | 5 +-
.../apijson/orm/model/AllColumnComment.java | 5 +-
.../main/java/apijson/orm/model/AllTable.java | 5 +-
.../apijson/orm/model/AllTableComment.java | 5 +-
.../main/java/apijson/orm/model/Column.java | 5 +-
.../main/java/apijson/orm/model/Document.java | 8 +-
.../apijson/orm/model/ExtendedProperty.java | 5 +-
.../main/java/apijson/orm/model/Function.java | 5 +-
.../java/apijson/orm/model/PgAttribute.java | 5 +-
.../main/java/apijson/orm/model/PgClass.java | 5 +-
.../main/java/apijson/orm/model/Request.java | 5 +-
.../main/java/apijson/orm/model/Script.java | 5 +-
.../java/apijson/orm/model/SysColumn.java | 5 +-
.../main/java/apijson/orm/model/SysTable.java | 5 +-
.../main/java/apijson/orm/model/Table.java | 5 +-
.../java/apijson/orm/model/TestRecord.java | 107 ------------------
20 files changed, 94 insertions(+), 130 deletions(-)
create mode 100755 APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java
delete mode 100644 APIJSONORM/src/main/java/apijson/orm/model/TestRecord.java
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
index c3532314..05e9f04e 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java
@@ -142,8 +142,6 @@ public abstract class AbstractSQLConfig, L exte
CONFIG_TABLE_LIST.add(Request.class.getSimpleName());
CONFIG_TABLE_LIST.add(Access.class.getSimpleName());
CONFIG_TABLE_LIST.add(Document.class.getSimpleName());
- CONFIG_TABLE_LIST.add(TestRecord.class.getSimpleName());
-
DATABASE_LIST = new ArrayList<>();
DATABASE_LIST.add(DATABASE_MYSQL);
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index 81ccc1ee..81bef3cd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -48,7 +48,6 @@
import apijson.orm.model.AllColumn;
import apijson.orm.model.AllTableComment;
import apijson.orm.model.AllColumnComment;
-import apijson.orm.model.TestRecord;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
@@ -174,7 +173,6 @@ public abstract class AbstractVerifier, L exten
SYSTEM_ACCESS_MAP.put(ExtendedProperty.class.getSimpleName(), getAccessMap(ExtendedProperty.class.getAnnotation(MethodAccess.class)));
SYSTEM_ACCESS_MAP.put(Document.class.getSimpleName(), getAccessMap(Document.class.getAnnotation(MethodAccess.class)));
- SYSTEM_ACCESS_MAP.put(TestRecord.class.getSimpleName(), getAccessMap(TestRecord.class.getAnnotation(MethodAccess.class)));
}
ACCESS_MAP = new HashMap<>(SYSTEM_ACCESS_MAP);
diff --git a/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java b/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java
new file mode 100755
index 00000000..f8935516
--- /dev/null
+++ b/APIJSONORM/src/main/java/apijson/orm/ConfigMethodAccess.java
@@ -0,0 +1,29 @@
+/*Copyright (C) 2020 Tencent. All rights reserved.
+
+This source code is licensed under the Apache License Version 2.0.*/
+
+
+//package apijson.orm;
+//
+//import apijson.MethodAccess;
+//
+//import java.lang.annotation.Documented;
+//import java.lang.annotation.Inherited;
+//import java.lang.annotation.Retention;
+//import java.lang.annotation.Target;
+//
+//import static apijson.orm.AbstractVerifier.*;
+//import static java.lang.annotation.ElementType.TYPE;
+//import static java.lang.annotation.RetentionPolicy.RUNTIME;
+//
+///**配置表的请求方法权限,只允许某些角色通过对应方法访问
+// 不能直接查到 MethodAccess,需要往上递归查找
+// * @author Lemon
+// */
+//@Documented
+//@Retention(RUNTIME)
+//@Target(TYPE)
+//@Inherited
+//@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
+//public @interface ConfigMethodAccess {
+//}
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Access.java b/APIJSONORM/src/main/java/apijson/orm/model/Access.java
index ab44f866..db934ec9 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/Access.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Access.java
@@ -7,9 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**访问权限
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
+//@ConfigMethodAccess
public class Access {
}
\ No newline at end of file
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/AllColumn.java b/APIJSONORM/src/main/java/apijson/orm/model/AllColumn.java
index 02906c6e..f4d93962 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/AllColumn.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/AllColumn.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 在 sys 下的字段(列名)
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class AllColumn {
public static final String TAG = "AllColumn";
public static final String TABLE_NAME = "ALL_TAB_COLUMNS";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/AllColumnComment.java b/APIJSONORM/src/main/java/apijson/orm/model/AllColumnComment.java
index 81e2c9fe..cf1480f7 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/AllColumnComment.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/AllColumnComment.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 在 sys 下的字段(列名)
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class AllColumnComment {
public static final String TAG = "AllColumnComment";
public static final String TABLE_NAME = "ALL_COL_COMMENTS";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/AllTable.java b/APIJSONORM/src/main/java/apijson/orm/model/AllTable.java
index 2934ad0b..dcc3268c 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/AllTable.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/AllTable.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 表属性
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class AllTable {
public static final String TAG = "AllTable";
public static final String TABLE_NAME = "ALL_TABLES";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/AllTableComment.java b/APIJSONORM/src/main/java/apijson/orm/model/AllTableComment.java
index 49a4dee3..ed61a0b4 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/AllTableComment.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/AllTableComment.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 表属性
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class AllTableComment {
public static final String TAG = "AllTableComment";
public static final String TABLE_NAME = "ALL_TAB_COMMENTS";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Column.java b/APIJSONORM/src/main/java/apijson/orm/model/Column.java
index 573ab7fc..800b60eb 100755
--- a/APIJSONORM/src/main/java/apijson/orm/model/Column.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Column.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**字段(列名)属性
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class Column {
public static final String TAG = "Column";
public static final String TABLE_NAME = "columns";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Document.java b/APIJSONORM/src/main/java/apijson/orm/model/Document.java
index 2e8db19c..1b2bc5d1 100755
--- a/APIJSONORM/src/main/java/apijson/orm/model/Document.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Document.java
@@ -17,10 +17,10 @@
* @author Lemon
*/
@MethodAccess(
- GET = { LOGIN, ADMIN },
- HEAD = { LOGIN, ADMIN },
- PUT = { LOGIN, ADMIN }
- )
+ GET = { LOGIN, ADMIN },
+ HEAD = { LOGIN, ADMIN },
+ PUT = { LOGIN, ADMIN }
+)
public class Document implements Serializable {
private static final long serialVersionUID = 1L;
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/ExtendedProperty.java b/APIJSONORM/src/main/java/apijson/orm/model/ExtendedProperty.java
index 393a7e0d..cc6e04ee 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/ExtendedProperty.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/ExtendedProperty.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**扩展属性,SQL Server 转用
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class ExtendedProperty {
public static final String TAG = "ExtendedProperty";
public static final String TABLE_NAME = "extended_properties";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Function.java b/APIJSONORM/src/main/java/apijson/orm/model/Function.java
index da6c3f53..841e6424 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/Function.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Function.java
@@ -7,9 +7,12 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**远程函数
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class Function {
}
\ No newline at end of file
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/PgAttribute.java b/APIJSONORM/src/main/java/apijson/orm/model/PgAttribute.java
index dbd4b4b6..174b3db0 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/PgAttribute.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/PgAttribute.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**PostgreSQL 字段属性
* @author Lemon
*/
-@MethodAccess
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class PgAttribute {
public static final String TAG = "PgAttribute";
public static final String TABLE_NAME = "pg_attribute";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/PgClass.java b/APIJSONORM/src/main/java/apijson/orm/model/PgClass.java
index 199a7ef8..e58feb69 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/PgClass.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/PgClass.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**PostgreSQL 表属性
* @author Lemon
*/
-@MethodAccess
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class PgClass {
public static final String TAG = "PgClass";
public static final String TABLE_NAME = "pg_class";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Request.java b/APIJSONORM/src/main/java/apijson/orm/model/Request.java
index 5d0d0409..46be3d30 100755
--- a/APIJSONORM/src/main/java/apijson/orm/model/Request.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Request.java
@@ -7,9 +7,12 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**请求处理
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class Request {
}
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Script.java b/APIJSONORM/src/main/java/apijson/orm/model/Script.java
index 53cda432..51205db0 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/Script.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Script.java
@@ -7,9 +7,12 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**代码脚本
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class Script {
}
\ No newline at end of file
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/SysColumn.java b/APIJSONORM/src/main/java/apijson/orm/model/SysColumn.java
index a4d7ca88..534446de 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/SysColumn.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/SysColumn.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 在 sys 下的字段(列名)
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class SysColumn {
public static final String TAG = "SysColumn";
public static final String TABLE_NAME = "columns";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/SysTable.java b/APIJSONORM/src/main/java/apijson/orm/model/SysTable.java
index bb614231..7b1eb74c 100644
--- a/APIJSONORM/src/main/java/apijson/orm/model/SysTable.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/SysTable.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**SQL Server 表属性
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class SysTable {
public static final String TAG = "SysTable";
public static final String TABLE_NAME = "tables";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/Table.java b/APIJSONORM/src/main/java/apijson/orm/model/Table.java
index 1ab004cc..45df12fd 100755
--- a/APIJSONORM/src/main/java/apijson/orm/model/Table.java
+++ b/APIJSONORM/src/main/java/apijson/orm/model/Table.java
@@ -7,10 +7,13 @@
import apijson.MethodAccess;
+import static apijson.orm.AbstractVerifier.ADMIN;
+import static apijson.orm.AbstractVerifier.LOGIN;
+
/**表属性
* @author Lemon
*/
-@MethodAccess(POST = {}, PUT = {}, DELETE = {})
+@MethodAccess(GET = {LOGIN, ADMIN}, HEAD = {LOGIN, ADMIN}, POST = {}, PUT = {}, DELETE = {})
public class Table {
public static final String TAG = "Table";
public static final String TABLE_NAME = "tables";
diff --git a/APIJSONORM/src/main/java/apijson/orm/model/TestRecord.java b/APIJSONORM/src/main/java/apijson/orm/model/TestRecord.java
deleted file mode 100644
index 32ad98f9..00000000
--- a/APIJSONORM/src/main/java/apijson/orm/model/TestRecord.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*Copyright (C) 2020 Tencent. All rights reserved.
-
-This source code is licensed under the Apache License Version 2.0.*/
-
-
-package apijson.orm.model;
-
-import static apijson.orm.AbstractVerifier.ADMIN;
-import static apijson.orm.AbstractVerifier.LOGIN;
-
-import java.io.Serializable;
-import java.sql.Timestamp;
-
-import apijson.MethodAccess;
-
-/**测试结果。5.0.0 之后可能改名为 Test
- * @author Lemon
- */
-@MethodAccess(GET = { LOGIN, ADMIN }, HEAD = { LOGIN, ADMIN })
-public class TestRecord implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private Long id; //唯一标识
- private Long userId; //用户id
- private Long documentId; //测试用例文档id
- private Timestamp date; //创建日期
- private String compare; //对比结果
- private String response; //接口返回结果JSON 用json格式会导致强制排序,而请求中引用赋值只能引用上面的字段,必须有序。
- private String standard; //response 的校验标准,是一个 JSON 格式的 AST ,描述了正确 Response 的结构、里面的字段名称、类型、长度、取值范围 等属性。
-
-
- public TestRecord() {
- super();
- }
- public TestRecord(long id) {
- this();
- setId(id);
- }
-
-
-
-
- public Long getId() {
- return id;
- }
-
- public TestRecord setId(Long id) {
- this.id = id;
- return this;
- }
-
- public Long getUserId() {
- return userId;
- }
-
- public TestRecord setUserId(Long userId) {
- this.userId = userId;
- return this;
- }
-
- public Long getDocumentId() {
- return documentId;
- }
-
- public TestRecord setDocumentId(Long documentId) {
- this.documentId = documentId;
- return this;
- }
-
- public Timestamp getDate() {
- return date;
- }
-
- public TestRecord setDate(Timestamp date) {
- this.date = date;
- return this;
- }
-
- public String getCompare() {
- return compare;
- }
-
- public TestRecord setCompare(String compare) {
- this.compare = compare;
- return this;
- }
-
- public String getResponse() {
- return response;
- }
-
- public TestRecord setResponse(String response) {
- this.response = response;
- return this;
- }
-
- public String getStandard() {
- return standard;
- }
-
- public TestRecord setStandard(String standard) {
- this.standard = standard;
- return this;
- }
-
-
- }
\ No newline at end of file
From 6b7c4d586341d70bd0416a0730e7db16784274ae Mon Sep 17 00:00:00 2001
From: TommyLemon
Date: Sat, 16 May 2026 23:49:27 +0800
Subject: [PATCH 87/98] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E4=B8=BA=208.1.8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
APIJSONORM/pom.xml | 2 +-
APIJSONORM/src/main/java/apijson/Log.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index fb050e32..7a35eb01 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.TencentAPIJSON
- 8.1.7
+ 8.1.8jarAPIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index e4bd7ba7..f00e495a 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -12,6 +12,7 @@
*/
public class Log {
public static boolean DEBUG = false;
+ public static final String VERSION = "8.1.8";
public static final String LEVEL_VERBOSE = "VERBOSE";
public static final String LEVEL_INFO = "INFO";
@@ -21,7 +22,6 @@ public class Log {
public static String LEVEL = LEVEL_WARN;
- public static final String VERSION = "8.1.7";
public static final String KEY_SYSTEM_INFO_DIVIDER = "\n---|-----APIJSON SYSTEM INFO-----|---\n";
public static final String OS_NAME;
From e321a94fc2cc4b85cdcb8cd3c6c0bf0ea69c811d Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 17 May 2026 01:00:53 +0800
Subject: [PATCH 88/98] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=87=E7=AB=A0=20?=
=?UTF-8?q?=E7=9C=81=E5=BF=83=E7=9C=81=E5=8A=9B=E7=9A=84=E5=90=8E=E7=AB=AF?=
=?UTF-8?q?=E7=A5=9E=E5=99=A8=E2=80=94=E2=80=94APIJSON.NET?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
感谢 妙堂传奇 的贡献,点赞、收藏、转发 支持下热心的作者吧 ^_^
https://mp.weixin.qq.com/s/8-E-a18NttdA0AAqasE0AQ
---
README-Chinese.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README-Chinese.md b/README-Chinese.md
index 8fd9e98c..f866e0bc 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -539,6 +539,8 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[3分钟掌握APIJSON搜索黑科技:从模糊匹配到智能检索](https://blog.csdn.net/gitblog_00009/article/details/152403741)
+[省心省力的后端神器——APIJSON.NET](https://mp.weixin.qq.com/s/8-E-a18NttdA0AAqasE0AQ)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
From 441e1fa1716d250785f0f1b34f2b9c8d090d6c8f Mon Sep 17 00:00:00 2001
From: Nguyen Van Nam
Date: Sun, 17 May 2026 03:49:38 +0700
Subject: [PATCH 89/98] fix(security): unsandboxed jsr223 script execution
enables arbitr
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
JSR223ScriptExecutor.load() compiles arbitrary script strings via Compilable.compile() and execute() runs them via eval() with no ClassFilter, sandbox, or restricted ScriptContext. The bindings expose `_meta`, `args`, and `extParam`, but Nashorn/JS engines by default give scripts full access to Java reflection (e.g., Java.type('java.lang.Runtime').getRuntime().exec(...)). Comments in Operation.java explicitly warn 'JDK 8~13 可用自带 Nashorn 这个 js 引擎,注意配置 ClassFilter 防脚本注入攻击', but no ClassFilter is configured here. If script content is sourced from a database row, request payload, or any user-influenced channel (which the IF/CODE Operation suggests), this becomes RCE.
Affected files: JSR223ScriptExecutor.java
Signed-off-by: Nguyen Van Nam
---
.../orm/script/JSR223ScriptExecutor.java | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
index 9c2c9baf..7e08a945 100644
--- a/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/script/JSR223ScriptExecutor.java
@@ -27,11 +27,34 @@ public abstract class JSR223ScriptExecutor, L e
@Override
public ScriptExecutor init() {
- ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
- scriptEngine = scriptEngineManager.getEngineByName(scriptEngineName());
+ scriptEngine = createScriptEngine();
return this;
}
+ protected ScriptEngine createScriptEngine() {
+ String name = scriptEngineName();
+ if ("nashorn".equalsIgnoreCase(name) || "javascript".equalsIgnoreCase(name)
+ || "js".equalsIgnoreCase(name) || "ecmascript".equalsIgnoreCase(name)) {
+ try {
+ Class> factoryClass = Class.forName("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+ Class> filterClass = Class.forName("jdk.nashorn.api.scripting.ClassFilter");
+ Object filter = java.lang.reflect.Proxy.newProxyInstance(
+ filterClass.getClassLoader(),
+ new Class>[]{filterClass},
+ (proxy, method, methodArgs) -> isClassExposureAllowed((String) methodArgs[0]));
+ Object factory = factoryClass.getDeclaredConstructor().newInstance();
+ return (ScriptEngine) factoryClass.getMethod("getScriptEngine", filterClass).invoke(factory, filter);
+ } catch (Throwable e) {
+ Log.e(TAG, "create sandboxed Nashorn engine failed, falling back: " + e);
+ }
+ }
+ return new ScriptEngineManager().getEngineByName(name);
+ }
+
+ protected boolean isClassExposureAllowed(String className) {
+ return false;
+ }
+
protected abstract String scriptEngineName();
protected abstract Object extendParameter(AbstractFunctionParser parser, Map currentObject, String methodName, Object[] args);
From bf4ef186c62c484d0d134dc238ed6194f51ff636 Mon Sep 17 00:00:00 2001
From: Nguyen Van Nam
Date: Sun, 17 May 2026 03:52:07 +0700
Subject: [PATCH 90/98] =?UTF-8?q?fix:=20resolve=20#853=20=E2=80=94=20[Feat?=
=?UTF-8?q?ure]=208.x=E7=89=88=E6=9C=AC=E6=B2=A1=E6=9C=89Demo=E5=90=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes #853
Signed-off-by: Nguyen Van Nam
---
APIJSONORM/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/README.md b/APIJSONORM/README.md
index 0cb431e2..8aa82f2a 100644
--- a/APIJSONORM/README.md
+++ b/APIJSONORM/README.md
@@ -21,7 +21,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
com.github.TencentAPIJSON
- LATEST
+ 8.0.0
```
@@ -45,7 +45,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
#### 2. Add the APIJSON dependency in one of your modules(such as `app`)
```gradle
dependencies {
- implementation 'com.github.Tencent:APIJSON:latest'
+ implementation 'com.github.Tencent:APIJSON:8.0.0'
}
```
From a009c85443d26f4300cc788e567ae2e0ac0d8e95 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Sun, 24 May 2026 10:32:56 +0800
Subject: [PATCH 91/98] readme: replace version to latest, thx to Nam0101 #859
https://github.com/Tencent/APIJSON/pull/859
---
APIJSONORM/README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/APIJSONORM/README.md b/APIJSONORM/README.md
index 8aa82f2a..745733b4 100644
--- a/APIJSONORM/README.md
+++ b/APIJSONORM/README.md
@@ -21,7 +21,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
com.github.TencentAPIJSON
- 8.0.0
+ 8.1.8
```
@@ -45,7 +45,7 @@ Tencent [APIJSON](https://github.com/Tencent/APIJSON) ORM library for remote dep
#### 2. Add the APIJSON dependency in one of your modules(such as `app`)
```gradle
dependencies {
- implementation 'com.github.Tencent:APIJSON:8.0.0'
+ implementation 'com.github.Tencent:APIJSON:8.1.8'
}
```
From 8f2c951a317399d46a9876690ac4e682c73fa319 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 8 Jun 2026 00:12:47 +0800
Subject: [PATCH 92/98] Update README-Chinese.md
---
README-Chinese.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README-Chinese.md b/README-Chinese.md
index f866e0bc..9737564f 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -650,7 +650,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[xyerp](https://gitee.com/yinjg1997/xyerp) 基于ApiJson的低代码ERP
-[quick-boot](https://github.com/csx-bill/quick-boot) 基于 Spring Cloud 2022、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
+[quick-boot](https://github.com/csx-bill/quick-boot/tree/master) 基于 Spring Cloud 2022、Spring Boot 3、AMIS 和 APIJSON 的低代码系统。
[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) 一个快速构建 APIJSON 查询条件的插件
From 5789d667e4c36edb2ca124d793fa8a595c2a1f81 Mon Sep 17 00:00:00 2001
From: Zhengcy05 <1825478405@qq.com>
Date: Thu, 11 Jun 2026 17:25:50 +0800
Subject: [PATCH 93/98] fix: fix global @explain not effective
---
APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index c4f7c5ff..5fd00c17 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -375,7 +375,7 @@ else if (_method == PUT && value instanceof List> && (whereList == null || whe
if (isSubquery == false) { // 解决 SQL 语法报错,子查询不能 EXPLAIN
Boolean exp = parser.getGlobalExplain();
- if (sch != null) {
+ if (exp != null) {
sqlRequest.putIfAbsent(JSONMap.KEY_EXPLAIN, exp);
}
From 0f0231dd90bf2d0c093282c736e1594d45ff6106 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 23 Jun 2026 21:56:05 +0800
Subject: [PATCH 94/98] Add Ecosystem for README.md:
https://github.com/Tencent/APIJSON#ecosystem
---
README.md | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 133 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index f6c637fd..cff65b99 100644
--- a/README.md
+++ b/README.md
@@ -141,7 +141,7 @@ You're gonna leave 'em all in awe, awe, awe.
**Tired with endless arguments about HTTP API dev or use?**
**Use APIJSON-the ORM for providing infinity codeless CRUD APIs that fit almost all your needs.**
-**Unfold the Power(In Your Soul) with ⭐Star & Clone.**
+**Unfold the Power(In Your Soul) with ⭐ Star & Clone.**
### APIJSON Show
#### Postman test APIJSON
@@ -215,7 +215,7 @@ Please have a look at the [open issues](https://github.com/Tencent/APIJSON/issue
Fork the project and send a pull request.
-Please also ⭐Star the project!
+### Please also ⭐ Star(on the top right) this project!
##
5. Releases
@@ -322,5 +322,136 @@ a lot of employees from big famous companies(Tencent, Huawei, Microsoft, Zoom, e
+### Ecosystem
+[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) Demo projects with document and SQL files for APIJSON with different programming languages and different frameworks
+
+[apijson-orm](https://github.com/APIJSON/apijson-orm) APIJSON ORM library, Maven, Gradle, etc can be used for dependencies
+
+[apijson-framework](https://github.com/APIJSON/apijson-framework) APIJSON Server Framework for configuring access of roles and validation of arguments in database tables, then using APIJSON easier
+
+[apijson-router](https://github.com/APIJSON/apijson-router) A router plugin for APIJSON, expose undercontrolled RESTful-like HTTP API to public network, transfer to APIJSON request and execute
+
+[apijson-column](https://github.com/APIJSON/apijson-column) A column plugin for Tencent APIJSON, supports Column Inverse and Column Mapping
+
+[apijson-jackson](https://github.com/APIJSON/apijson-jackson) A jackson plugin for APIJSON
+
+[apijson-fastjson2](https://github.com/APIJSON/apijson-fastjson2) A fastjson2 plugin for APIJSON
+
+[apijson-gson](https://github.com/APIJSON/apijson-gson) A gson plugin for APIJSON
+
+[apijson-milvus](https://github.com/APIJSON/apijson-milvus) An APIJSON plugin for Milvus - An AI vector database
+
+[apijson-influxdb](https://github.com/APIJSON/apijson-influxdb) An APIJSON plugin for InfluxDB - An IoT time-series database
+
+[apijson-mongodb](https://github.com/APIJSON/apijson-mongodb) An APIJSON plugin for MongoDB - A NoSQL database
+
+[apijson-cassandra](https://github.com/APIJSON/apijson-cassandra) An APIJSON plugin for Cassandra - A NoSQL database
+
+[APIAuto](https://github.com/TommyLemon/APIAuto) ☔ The most advanced tool for HTTP API. Machine learning no-code testing and AI assistant, generating codes and static analysis, generating comments and floating hints. Used by Tencent, SHEIN, TRANSSION, etc
+
+[CVAuto](https://github.com/TommyLemon/CVAuto) 👁 No-code, zero-annotation CV(Computer Vision) AI automated testing tool 🚀 Eliminates the need for extensive manual tasks such as drawing bounding boxes and labeling for image recognition algorithms
+
+[UnitAuto](https://github.com/TommyLemon/UnitAuto) ☀️ The most advanced unit testing way powered by machine learning. Coding-free, comprehensive and automatic testing for methods/functions. Used by Tencent, Kwai, a Fortune 500 company, etc
+
+[SQLAuto](https://github.com/TommyLemon/SQLAuto) 🔍 A smart SQL testing automation tool for databases, supports any CRUD, any template variables, generating argument combinations, generating lots of data rows
+
+[UIGO](https://github.com/TommyLemon/UIGO) 📱 Coding-free, fast, accurate and stable UI replayer 🚀 Incredible ±3px auto locating and ±2ms auto waiting. Used by Tencent, invited by WeChat team to share
+
+[APIJSONdocs](https://github.com/ruoranw/APIJSONdocs) APIJSON English documentation, provided a website to view
+
+[apijson-doc](https://github.com/vincentCheng/apijson-doc) APIJSON Chinese documentation, provided a website to view
+
+[apijson.org](https://github.com/APIJSON/apijson.org) APIJSON official website
+
+[APIJSON.NET](https://github.com/liaozb/APIJSON.NET) APIJSON for C#, supports CRUD for MySQL, PostgreSQL, SQL Server, Oracle, SQLite
+
+[apijson-go](https://github.com/glennliao/apijson-go) APIJSON for Go, based on Go(>=1.18) + GoFrame2, support CRUD
+
+[apijson-go](https://gitee.com/tiangao/apijson-go) APIJSON for Go, support CRUD, can directly run in Docker
+
+[apijson-hyperf](https://github.com/kvnZero/hyperf-APIJSON.git) APIJSON for PHP, based on Hyperf, supports MySQL
+
+[APIJSON-php](https://github.com/xianglong111/APIJSON-php) APIJSON for PHP, based on ThinkPHP,supports MySQL, PostgreSQL, SQL Server, Oracle, etc
+
+[apijson-php](https://github.com/qq547057827/apijson-php) APIJSON for PHP, based on ThinkPHP,supports MySQL, PostgreSQL, SQL Server, Oracle, etc
+
+[apijson-node](https://github.com/kevinaskin/apijson-node) APIJSON for Node.js, developed by a ByteDance engineer, provides demos for NestJS and Typeorm, as well as backend management
+
+[nestjs-apijson](https://github.com/yangzhouQS/nestjs-apijson) APIJSON for NestJS, supports CRUD, Joins for MySQL, PostgreSQL, SQLite (AI assisted)
+
+[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) APIJSON for Python, supports CRUD for MySQL, PostgreSQL, SQL Server, Oracle, SQLite, etc
+
+[apijson-rust](https://github.com/APIJSON/apijson-rust) APIJSON for Rust, supports CRUD for MySQL and PostgreSQL (AI assisted)
+
+[APIJSONParser](https://github.com/Zerounary/APIJSONParser) An APIJSON Parser, dynamically parses JSON to SQL
+
+[FfApiJson](https://gitee.com/own_3_0/ff-api-json) An APIJSON Parser, parses JSON to SQL, supports multiple data sources
+
+[APIJSON-ToDo-Demo](https://github.com/jerrylususu/apijson_todo_demo) A simple TODO demo for APIJSON with customized authorization and authentication
+
+[apijson-learn](https://github.com/rainboy-learn/apijson-learn) APIJSON study note and analysis for source code
+
+[apijson-practice](https://github.com/vcoolwind/apijson-practice) A library for APIJSON parameter validation annotations and related demos, open-sourced by a BAT expert
+
+[apijson-db2](https://github.com/andream7/apijson-db2) Demo of APIJSON + IBM DB2 database, open-sourced by a Microsoft engineer
+
+[APIJSONDemo](https://github.com/qiujunlin/APIJSONDemo) Demo of APIJSON + ClickHouse database, open-sourced by a ByteDance engineer
+
+[APIJSONDemo_ClickHouse](https://github.com/chenyanlann/APIJSONDemo_ClickHouse) Demo for APIJSON + SpringBoot + ClickHouse
+
+[APIJSONBoot_Hive](https://github.com/chenyanlann/APIJSONBoot_Hive) Demo for APIJSON + SpringBoot + Hive
+
+[apijson-sample](https://gitee.com/greyzeng/apijson-sample) Simple demo and tutorial for using APIJSON
+
+[apijson-examples](https://gitee.com/drone/apijson-examples) Demo for APIJSON with frontend web page, backend server and management system
+
+[apijson-ruoyi](https://github.com/daodol/apijson-ruoyi) APIJSON + RuoYi, provides online maintenance for database configuration, etc
+
+[light4j](https://github.com/xlongwei/light4j) Demo for APIJSON + Redis + light-4j - a microservices framework
+
+[SpringServer1.2-APIJSON](https://github.com/Airforce-1/SpringServer1.2-APIJSON) The smart Party building server provides interfaces for uploading and downloading files
+
+[apijson_template](https://github.com/abliger/apijson_template) APIJSON Java template, using Gradle to manage dependencie and building apps
+
+[api-json-demo](https://gitee.com/hxdwd/api-json-demo) Demo for APIJSON to replace traditional ORM, compated Oracle transactions
+
+[ApiJsonByJFinal](https://gitee.com/zhiyuexin/ApiJsonByJFinal) Demo for APIJSON + JFinal - a popular web framework
+
+[bookmark](https://github.com/glennliao/bookmark) Online bookmark using goframe + apijson-go + vue3 + antd vue 4
+
+[apijson-go-demo](https://github.com/APIJSON/apijson-go-demo) Demo for apijson-go
+
+[apijson-go-ui](https://github.com/glennliao/apijson-go-ui) apijson-go UI config, supports access control, request rule configuration, etc
+
+[apijson-builder](https://github.com/pengxianggui/apijson-builder) A JavaScript client library providing RESTful-like functions for APIJSON
+
+[AbsGrade](https://github.com/APIJSON/AbsGrade) List cascading algorithm supports single-level comments in WeChat Moments, double-level comments in QQ Space, and multi-level (unlimited-level) folders in Baidu Cloud
+
+[APIJSON-Android-RxJava](https://github.com/TommyLemon/APIJSON-Android-RxJava) Practical project mimicking WeChat Moments updates, based on ZBLibrary(UI) + APIJSON(HTTP) + RxJava(Data)
+
+[Android-ZBLibrary](https://github.com/TommyLemon/Android-ZBLibrary) 🔥 An Android MVP Framework with many demos, detailed documents, simple usages and strict codes
+
+[apijson-dynamic-datasource](https://github.com/wb04307201/apijson-dynamic-datasource) Based on APIJSON, this demo demonstrates dynamic data source switching and batch operations on the same data source to ensure transaction consistency
+
+[xyerp](https://gitee.com/yinjg1997/xyerp) Low-code ERP based on APIJSON
+
+[quick-boot](https://github.com/csx-bill/quick-boot/tree/master) Low-code system based on Spring Cloud 2022 + Spring Boot 3 + AMIS + APIJSON
+
+[apijson-query-spring-boot-starter](https://gitee.com/mingbaobaba/apijson-query-spring-boot-starter) A plugin for quickly building APIJSON query conditions
+
+[apijson-builder](https://github.com/yeli19950109/apijson-builder) A simplified TypeScript wrapper for APIJSON, easier to remember than directly constructing query JSON
+
+[lanmuc](https://gitee.com/element-admin/lanmuc) A platform for producing low-code backend APIs, compatible with both configuration-based and code-based APIs, enabling rapid API production and project deployment
+
+[review_plan](https://gitee.com/PPXcodeTry/review_plan) Review Reminder Web Version (Java Technology Practice Project)
+
+[apijson-nutz](https://github.com/vincent109/apijson-nutz) Demo for APIJSON + Nutz + NutzBoot
+
+[apijson-spring-boot](https://gitee.com/yunjiao-source/apijson-spring-boot) Springboot3 for APIJSON, using YAML to simplify configuration
+
+[APIJSONServer](https://github.com/cyber2jie/APIJSONServer) Data server based on APIJSON
+
+Thank you to all the enthusiastic authors for the contributions~
+### Please give them a ⭐ Star(on the top right) to support their hard works!
From 9606842f0feba9e5b031ac383280e101bd3a00e1 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Tue, 23 Jun 2026 22:06:17 +0800
Subject: [PATCH 95/98] =?UTF-8?q?=E7=94=9F=E6=80=81=E6=96=B0=E5=A2=9E=20ne?=
=?UTF-8?q?stjs-apijson=20-=20=E6=94=AF=E6=8C=81=20CRUD=E3=80=81JOIN?=
=?UTF-8?q?=E3=80=81=E5=90=84=E7=A7=8D=E6=9D=A1=E4=BB=B6=20(AI=20=E8=BE=85?=
=?UTF-8?q?=E5=8A=A9)=E3=80=81bookmark=20-=20goframe=20+=20apijson-go=20+?=
=?UTF-8?q?=20vue3=20+=20antd=20vue=204=20=E7=9A=84=E5=9C=A8=E7=BA=BF?=
=?UTF-8?q?=E4=B9=A6=E7=AD=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
创作不易,打开下方链接右上角点亮 ⭐️ Star 收藏/支持 下热心的作者们吧 ^_^
1.nestjs-apijson - APIJSON for NestJS,支持 CRUD、JOIN、各种条件 (AI 辅助)
https://github.com/yangzhouQS/nestjs-apijson
2.bookmark - goframe + apijson-go + vue3 + antd vue 4 的在线书签
https://github.com/glennliao/bookmark
---
README-Chinese.md | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/README-Chinese.md b/README-Chinese.md
index 9737564f..ad77e388 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -413,7 +413,7 @@ https://github.com/Tencent/APIJSON/blob/master/Roadmap.md
[OceanBase](https://www.oceanbase.com/docs/oceanbase/V2.2.50/ss-sr-select_daur3l), [Spark](https://spark.apache.org/docs/3.3.0/sql-ref-syntax-qry-select.html)(可用 Hive 对接), [Phoenix](http://phoenix.apache.org/language/index.html#select)(延伸支持 HBase)
### 我要赞赏
-创作不易,坚持更难,右上角点 ⭐Star 来支持/收藏下吧,谢谢 ^_^
+创作不易,坚持更难,右上角点亮 ⭐ Star 来收藏/支持下吧,谢谢 ^_^
https://github.com/Tencent/APIJSON
@@ -596,9 +596,11 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[apijson-node](https://github.com/kevinaskin/apijson-node) 字节跳动工程师开源的 Node.ts 版 APIJSON,提供 nestjs 和 typeorm 的 Demo 及后台管理
+[nestjs-apijson](https://github.com/yangzhouQS/nestjs-apijson) APIJSON NestJS 版,支持 CRUD、JOIN、各种条件,适配 MySQL, PostgreSQL, SQLite (AI 辅助)
+
[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) Python 版 APIJSON,支持 MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等
-[apijson-rust](https://github.com/APIJSON/apijson-rust) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL
+[apijson-rust](https://github.com/APIJSON/apijson-rust) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL (AI 辅助)
[APIJSONParser](https://github.com/Zerounary/APIJSONParser) 第三方 APIJSON 解析器,将 JSON 动态解析成 SQL
@@ -634,7 +636,9 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[ApiJsonByJFinal](https://gitee.com/zhiyuexin/ApiJsonByJFinal) 整合 APIJSON 和 JFinal 的 Demo
-[apijson-go-demo](https://github.com/glennliao/apijson-go-demo) apijson-go demos,提供 3 个从简单到复杂的不同场景 Demo
+[bookmark](https://github.com/glennliao/bookmark) goframe + apijson-go + vue3 + antd vue 4 的在线书签
+
+[apijson-go-demo](https://github.com/APIJSON/apijson-go-demo) apijson-go demos,提供 3 个从简单到复杂的不同场景 Demo
[apijson-builder](https://github.com/pengxianggui/apijson-builder) 一个方便为 APIJSON 构建 RESTful 请求的 JavaScript 库
@@ -666,7 +670,7 @@ Issue/问卷 一般解答顺序:贡献者 > 帮助他人的用户 > 提供任
[APIJSONServer](https://github.com/cyber2jie/APIJSONServer) 基于APIJSON实现的数据服务端
-感谢热心的作者们的贡献,点 ⭐Star 支持下他们吧~
+感谢热心的作者们的贡献,点亮 ⭐ Star 收藏/支持下他们吧~
### 腾讯犀牛鸟开源人才培养计划
From 14551cdc6b28f2954e03af91bcfa9f2416ce3c39 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 29 Jun 2026 00:19:59 +0800
Subject: [PATCH 96/98] AI: Create context7.json for LLM AI Skills
https://context7.com/tencent/apijson
---
context7.json | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 context7.json
diff --git a/context7.json b/context7.json
new file mode 100644
index 00000000..7059d93f
--- /dev/null
+++ b/context7.json
@@ -0,0 +1,4 @@
+{
+ "url": "https://context7.com/tencent/apijson",
+ "public_key": "pk_hqcWKqm5UmWzQouumDz3F"
+}
From 76cb2c46cce66fd03ca351d4e07470c499cdde85 Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 29 Jun 2026 01:04:12 +0800
Subject: [PATCH 97/98] =?UTF-8?q?doc:=20add=20LLM=20AI=20Skills,=20MCP,=20?=
=?UTF-8?q?A2A=20/=20=E6=96=87=E6=A1=A3=EF=BC=9A=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E5=A4=A7=E6=A8=A1=E5=9E=8B=20AI=20Skills,=20MCP,=20A2A=20?=
=?UTF-8?q?=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
感谢 Upstash Context7 和 YRO.ai
https://github.com/Tencent/APIJSON/issues/864
https://github.com/Tencent/APIJSON/issues/861
---
README.md | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index cff65b99..0bcb1841 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,9 @@ This source code is licensed under the Apache License Version 2.0 Video Test Ask AI
+ Skills
+ MCP
+ A2A
@@ -75,7 +78,7 @@ This source code is licensed under the Apache License Version 2.0
-
+
From 5ac449c03b8bde5c694445ec019f2d43d0acbdbb Mon Sep 17 00:00:00 2001
From: TommyLemon <1184482681@qq.com>
Date: Mon, 29 Jun 2026 01:06:02 +0800
Subject: [PATCH 98/98] =?UTF-8?q?doc:=20add=20LLM=20AI=20Skills,=20MCP,=20?=
=?UTF-8?q?A2A=20/=20=E6=96=87=E6=A1=A3=EF=BC=9A=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E5=A4=A7=E6=A8=A1=E5=9E=8B=20AI=20Skills,=20MCP,=20A2A=20?=
=?UTF-8?q?=E5=85=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Thanks to Upstash Context7 and YRO.ai ~
感谢 Upstash Context7 和 YRO.ai ~
https://github.com/Tencent/APIJSON/issues/864
https://github.com/Tencent/APIJSON/issues/861
---
README-Chinese.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README-Chinese.md b/README-Chinese.md
index ad77e388..c13f82e9 100644
--- a/README-Chinese.md
+++ b/README-Chinese.md
@@ -14,6 +14,9 @@ This source code is licensed under the Apache License Version 2.0 视频教程测试用例AI 问答
+ Skills
+ MCP
+ A2A