diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index bd340fa5f..8b36d0da4 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -5,7 +5,7 @@
com.github.Tencent
APIJSON
- 8.0.2
+ 8.1.3
jar
APIJSONORM
diff --git a/APIJSONORM/src/main/java/apijson/JSONMap.java b/APIJSONORM/src/main/java/apijson/JSONMap.java
index 29d88f756..3b93d69d2 100755
--- a/APIJSONORM/src/main/java/apijson/JSONMap.java
+++ b/APIJSONORM/src/main/java/apijson/JSONMap.java
@@ -109,19 +109,32 @@ public static boolean isTableArray(String key) {
public static String KEY_USER_ID = "userId";
public static String KEY_USER_ID_IN = KEY_USER_ID + "{}";
+ default String getIdKey() {
+ return KEY_ID;
+ }
+ default String getIdInKey() {
+ return KEY_ID_IN;
+ }
+ default String getUserIdKey() {
+ return KEY_USER_ID;
+ }
+ default String getUserIdInKey() {
+ return KEY_USER_ID_IN;
+ }
+
/**set "id":id in Table layer
* @param id
* @return
*/
default JSONMap setId(Long id) {
- return puts(KEY_ID, id);
+ return puts(getIdKey(), id);
}
/**set "id{}":[] in Table layer
* @param list
* @return
*/
default JSONMap setIdIn(List list) {
- return puts(KEY_ID_IN, list);
+ return puts(getIdInKey(), list);
}
/**set "userId":userId in Table layer
@@ -129,14 +142,14 @@ default JSONMap setIdIn(List list) {
* @return
*/
default JSONMap setUserId(Long id) {
- return puts(KEY_USER_ID, id);
+ return puts(getUserIdKey(), id);
}
/**set "userId{}":[] in Table layer
* @param list
* @return
*/
default JSONMap setUserIdIn(List list) {
- return puts(KEY_USER_ID_IN, list);
+ return puts(getUserIdInKey(), list);
}
@@ -150,36 +163,37 @@ default JSONMap setUserIdIn(List list) {
//@key关键字都放这个类 <<<<<<<<<<<<<<<<<<<<<< - String KEY_TRY = "@try"; //尝试,忽略异常 - String KEY_CATCH = "@catch"; //TODO 捕捉到异常后,处理方式 null-不处理;DEFAULT-返回默认值;ORIGIN-返回请求里的原始值 - String KEY_DROP = "@drop"; //丢弃,不返回,TODO 应该通过 fastjson 的 ignore 之类的机制来处理,避免导致下面的对象也不返回 - // String KEY_KEEP = "@keep"; //一定会返回,为 null 或 空对象时,会使用默认值(非空),解决其它对象因为不关联的第一个对为空导致也不返回 - String KEY_DEFULT = "@default"; //TODO 自定义默认值 { "@default":true },@default 可完全替代 @keep - String KEY_NULL = "@null"; //值为 null 的键值对 "@null":"tag,pictureList",允许 is NULL 条件判断, SET tag = NULL 修改值为 NULL 等 - String KEY_CAST = "@cast"; //类型转换 cast(date AS DATE) - - String KEY_ROLE = "@role"; //角色,拥有对某些数据的某些操作的权限 - String KEY_DATABASE = "@database"; //数据库类型,默认为MySQL - String KEY_DATASOURCE = "@datasource"; //数据源 - String KEY_NAMESPACE = "@namespace"; //命名空间,Table 在非默认 namespace 内时需要声明 - String KEY_CATALOG = "@catalog"; //目录,Table 在非默认 catalog 内时需要声明 - String KEY_SCHEMA = "@schema"; //数据库,Table 在非默认 schema 内时需要声明 - String KEY_EXPLAIN = "@explain"; //分析 true/false - String KEY_CACHE = "@cache"; //缓存 RAM/ROM/ALL - String KEY_COLUMN = "@column"; //查询的Table字段或SQL函数 - String KEY_FROM = "@from"; //FROM语句 - String KEY_COMBINE = "@combine"; //条件组合,每个条件key前面可以放&,|,!逻辑关系 "id!{},&sex,!name&$" - String KEY_GROUP = "@group"; //分组方式 - String KEY_HAVING = "@having"; //聚合函数条件,一般和@group一起用 - String KEY_HAVING_AND = "@having&"; //聚合函数条件,一般和@group一起用 - String KEY_SAMPLE = "@sample"; //取样方式 - String KEY_LATEST = "@latest"; //最近方式 - String KEY_PARTITION = "@partition"; //分区方式 - String KEY_FILL = "@fill"; //填充方式 - String KEY_ORDER = "@order"; //排序方式 + String KEY_TRY = "@try"; // 尝试,忽略异常 + String KEY_CATCH = "@catch"; // TODO 捕捉到异常后,处理方式 null-不处理;DEFAULT-返回默认值;ORIGIN-返回请求里的原始值 + String KEY_DROP = "@drop"; // 丢弃,不返回,TODO 应该通过 fastjson 的 ignore 之类的机制来处理,避免导致下面的对象也不返回 + // String KEY_KEEP = "@keep"; // 一定会返回,为 null 或 空对象时,会使用默认值(非空),解决其它对象因为不关联的第一个对为空导致也不返回 + String KEY_DEFAULT = "@default"; // TODO 自定义默认值 { "@default":true },@default 可完全替代 @keep + String KEY_NULL = "@null"; // 值为 null 的键值对 "@null":"tag,pictureList",允许 is NULL 条件判断, SET tag = NULL 修改值为 NULL 等 + String KEY_CAST = "@cast"; // 类型转换 cast(date AS DATE) + + String KEY_ROLE = "@role"; // 角色,拥有对某些数据的某些操作的权限 + String KEY_DATABASE = "@database"; // 数据库类型,默认为MySQL + String KEY_DATASOURCE = "@datasource"; // 数据源 + String KEY_NAMESPACE = "@namespace"; // 命名空间,Table 在非默认 namespace 内时需要声明 + String KEY_CATALOG = "@catalog"; // 目录,Table 在非默认 catalog 内时需要声明 + String KEY_SCHEMA = "@schema"; // 数据库,Table 在非默认 schema 内时需要声明 + String KEY_EXPLAIN = "@explain"; // 分析 true/false + String KEY_CACHE = "@cache"; // 缓存 RAM/ROM/ALL + String KEY_COLUMN = "@column"; // 查询的 Table 字段或 SQL 函数 + String KEY_FROM = "@from"; // FROM语句 + String KEY_COMBINE = "@combine"; // 条件组合,每个条件 key 前面可以放 &,|,! 逻辑关系 "id!{},&sex,!name&$" + String KEY_GROUP = "@group"; // 分组方式 + String KEY_HAVING = "@having"; // 聚合函数条件,一般和 @group 一起用 + String KEY_HAVING_AND = "@having&"; // 聚合函数条件,一般和 @group 一起用 + String KEY_SAMPLE = "@sample"; // 取样方式 + String KEY_LATEST = "@latest"; // 最近方式 + String KEY_PARTITION = "@partition"; // 分区方式 + String KEY_FILL = "@fill"; // 填充方式 + String KEY_ORDER = "@order"; // 排序方式 String KEY_KEY = "@key"; // key 映射,year:left(date,4);name_tag:(name,tag) String KEY_RAW = "@raw"; // 自定义原始 SQL 片段 - String KEY_JSON = "@json"; //SQL Server 把字段转为 JSON 输出 + String KEY_JSON = "@json"; // 把字段转为 JSON 输出 + String KEY_STRING = "@string"; // 把字段转为 String 输入 String KEY_METHOD = "@method"; // json 对象配置操作方法 String KEY_GET = "@get"; // json 对象配置操作方法 String KEY_GETS = "@gets"; // json 对象配置操作方法 @@ -214,6 +228,7 @@ default JSONMap setUserIdIn(List list) {
KEY_KEY,
KEY_RAW,
KEY_JSON,
+ KEY_STRING,
KEY_METHOD,
KEY_GET,
KEY_GETS,
@@ -255,7 +270,7 @@ default JSONMap setDrop(Boolean drop) {
* @return this
*/
default JSONMap setDefault(Boolean hasDefault) {
- return puts(KEY_DEFULT, hasDefault);
+ return puts(KEY_DEFAULT, hasDefault);
}
@@ -525,6 +540,14 @@ default JSONMap setJson(String keys) {
return puts(KEY_JSON, keys);
}
+ /**set keys to cast to string
+ * @param keys "key0,key1,key2..."
+ * @return
+ */
+ default JSONMap setString(String keys) {
+ return puts(KEY_STRING, keys);
+ }
+
//JSONObject内关键词 key>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
diff --git a/APIJSONORM/src/main/java/apijson/JSONResponse.java b/APIJSONORM/src/main/java/apijson/JSONResponse.java
index c39aa1ace..7527adffb 100755
--- a/APIJSONORM/src/main/java/apijson/JSONResponse.java
+++ b/APIJSONORM/src/main/java/apijson/JSONResponse.java
@@ -119,7 +119,7 @@ public static String getMsg(Map response) {
*/
default long getId() {
try {
- return JSON.getLongValue(this, KEY_ID);
+ return JSON.getLongValue(this, getIdKey());
} catch (Exception e) {
//empty
}
diff --git a/APIJSONORM/src/main/java/apijson/Log.java b/APIJSONORM/src/main/java/apijson/Log.java
index ba1a03bd5..9cbacd0ac 100755
--- a/APIJSONORM/src/main/java/apijson/Log.java
+++ b/APIJSONORM/src/main/java/apijson/Log.java
@@ -14,7 +14,7 @@ public class Log {
public static boolean DEBUG = true;
- public static final String VERSION = "8.0.2";
+ public static final String VERSION = "8.1.3";
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/AbstractObjectParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
index 4d56499a0..0e6aaa784 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java
@@ -61,6 +61,7 @@ public AbstractObjectParser setParser(Parser parser) {
* TODO Parser内要不因为 非 TYPE_ITEM_CHILD_0 的Table 为空导致后续中断。
*/
protected final boolean drop;
+ private List stringKeyList;
/**for single object
*/
@@ -103,7 +104,12 @@ public AbstractObjectParser(@NotNull M request, String parentPath, SQLConfig parse(String name, boolean isReuse) throws
// hasOtherKeyNotFun = true;
// }
- if (startsWithAt || key.endsWith("@") || (key.endsWith("") && value instanceof Map, ?>)) {
+ 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();
+ }
+ }
+ else if (startsWithAt || key.endsWith("@") || (key.endsWith("") && value instanceof Map, ?>)) {
if (onParse(key, value) == false) {
invalidate();
}
@@ -1227,7 +1239,9 @@ public void recycle() {
if (drop) {
request.put(KEY_DROP, drop);
}
-
+ if (stringKeyList != null) { // 避免被全局关键词覆盖 && ! stringKeyList.isEmpty()) {
+ request.put(KEY_STRING, StringUtil.get(stringKeyList.toArray()));
+ }
method = null;
parentPath = null;
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
index 4d6d28c02..797ac3dee 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java
@@ -1315,7 +1315,7 @@ public Connection getConnection(@NotNull SQLConfig config) throws Excep
}
public String getConnectionKey(@NotNull SQLConfig config) {
- return getConnectionKey(config.getNamespace(), config.getCatalog(), config.getDatasource(), config.getDatabase());
+ return getConnectionKey(config.getDatabase(), config.getDatasource(), config.getNamespace(), config.getCatalog());
}
public String getConnectionKey(String database, String datasource, String namespace, String catalog) {
return database + "-" + datasource + "-" + namespace + "-" + catalog;
@@ -1541,3 +1541,4 @@ public int executeUpdate(@NotNull SQLConfig config, String sql) throws
}
+
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
index b10828eea..55eeff408 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractVerifier.java
@@ -6,6 +6,7 @@
package apijson.orm;
import static apijson.JSON.*;
+import static apijson.JSONMap.*;
import static apijson.RequestMethod.DELETE;
import static apijson.RequestMethod.GET;
import static apijson.RequestMethod.GETS;
@@ -219,7 +220,7 @@ public static HashMap getAccessMap(MethodAccess access)
@Override
public String getVisitorIdKey(SQLConfig config) {
- return config.getUserIdKey();
+ return config == null ? getUserIdKey(null, null, null, null) : config.getUserIdKey();
}
@Override
@@ -346,21 +347,23 @@ public void verifyAllowRole(SQLConfig config, String table, RequestMeth
* @throws Exception
* @see {@link JSONMap#KEY_ROLE}
*/
- public void verifyUseRole(SQLConfig config, String table, RequestMethod method, String role) throws Exception {
+ public void verifyUseRole(@NotNull SQLConfig config, String table, RequestMethod method, String role) throws Exception {
Log.d(TAG, "verifyUseRole table = " + table + "; method = " + method + "; role = " + role);
+ Objects.requireNonNull(config);
//验证角色,假定真实强制匹配<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< - String visitorIdKey = getVisitorIdKey(config); if (table == null) { - table = config == null ? null : config.getTable(); + table = config.getTable(); } if (method == null) { - method = config == null ? GET : config.getMethod(); + method = config.getMethod(); } if (role == null) { - role = config == null ? UNKNOWN : config.getRole(); + role = config.getRole(); } + String visitorIdKey = getVisitorIdKey(config); + Object requestId; switch (role) { case LOGIN://verifyRole通过就行 @@ -919,6 +922,16 @@ public static , L extends List> M parse
return null;
}
+ Object _if = target.get(IF.name());
+ boolean ifIsStr = _if instanceof String && StringUtil.isNotEmpty(_if, true);
+ M ifObj = ifIsStr == false && _if instanceof Map,?> ? (M) _if : null;
+// : (_if instanceof String ? new apijson.JSONMap((String) _if, "" /* "throw new Error('')" */ ) : null);
+ if (ifObj == null && _if != null && ifIsStr == false) {
+// if (_if instanceof List>) {
+// }
+ throw new IllegalArgumentException(name + ": { " + IF.name() + ": value } 中 value 类型错误!只允许 String, JSONRequest!");
+ }
+
// 获取配置<<<<<<<<<<<<<<<<<<<<<<<<<<<< M type = JSON.get(target, TYPE.name()); M verify = JSON.get(target, VERIFY.name()); @@ -932,21 +945,8 @@ public static , L extends List> M parse
String must = StringUtil.get(getString(target, MUST.name()));
String refuse = StringUtil.get(getString(target, REFUSE.name()));
- Object _if = target.get(IF.name());
- boolean ifIsStr = _if instanceof String && StringUtil.isNotEmpty(_if, true);
- M ifObj = ifIsStr == false && _if instanceof Map,?> ? (M) _if : null;
-// : (_if instanceof String ? new apijson.JSONMap((String) _if, "" /* "throw new Error('')" */ ) : null);
- if (ifObj == null && _if != null && ifIsStr == false) {
-// if (_if instanceof List>) {
-// }
- throw new IllegalArgumentException(name + ": { " + IF.name() + ": value } 中 value 类型错误!只允许 String, JSONRequest!");
- }
-
// Object code = target.get(CODE.name());
- String allowPartialUpdateFail = StringUtil.get(getString(target, ALLOW_PARTIAL_UPDATE_FAIL.name()));
-
-
// 移除字段<<<<<<<<<<<<<<<<<<< String[] removes = StringUtil.split(remove); if (removes != null && removes.length> 0) {
@@ -972,6 +972,8 @@ public static , L extends List> M parse
}
// 判断必要字段是否都有>>>>>>>>>>>>>>>>>>>
+ String[] sks = StringUtil.split(getString(real, KEY_STRING));
+ List stringKeyList = sks == null || sks.length <= 0 ? null : Arrays.asList(sks); Set objKeySet = new HashSet(); // 不能用tableKeySet,仅判断 Table:{} 会导致 key:{ Table:{} } 绕过判断
@@ -987,6 +989,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);
+ }
+
if (callback.onParse(key, tvalue, rvalue) == false) {
continue;
}
@@ -1087,17 +1093,29 @@ public static , L extends List> M parse
// 判断不允许传的key<<<<<<<<<<<<<<<<<<<<<<<<< for (String rk : rkset) { + if (rk == null || KEY_STRING.equals(rk)) { + // ConcurrentModificationException real.remove(rk); + continue; + } + if (refuseSet.contains(rk)) { // 不允许的字段 throw new IllegalArgumentException(method + "请求," + name + " 里面不允许传 " + rk + " 等" + StringUtil.get(refuseSet) + "内的任何字段!"); } - if (rk == null) { // 无效的key - real.remove(rk); - continue; + if (KEY_COMBINE.equals(rk)) { + throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!" + + "非开放请求不允许传 " + KEY_COMBINE + ":value !"); + } + if (KEY_KEY.equals(rk)) { + throw new UnsupportedOperationException(method + " 请求," + rk + " 不合法!" + + "非开放请求不允许传 " + KEY_KEY + ":value !"); } Object rv = real.get(rk); + if (rv != null && stringKeyList != null && stringKeyList.contains(rk)) { + rv = JSON.toJSONString(rv); + } // 不允许传远程函数,只能后端配置 if (rk.endsWith("()") && rv instanceof String) { @@ -1179,6 +1197,7 @@ public static , L extends List> M parse
// 校验重复>>>>>>>>>>>>>>>>>>>
// 校验并配置允许批量增删改部分失败<<<<<<<<<<<<<<<<<<< + String allowPartialUpdateFail = StringUtil.get(getString(target, ALLOW_PARTIAL_UPDATE_FAIL.name())); String[] partialFails = StringUtil.split(allowPartialUpdateFail); if (partialFails != null && partialFails.length> 0) {
for (String key : partialFails) {
@@ -1287,6 +1306,7 @@ public static , L extends List> M parse
}
}
}
+
Log.i(TAG, "parse return real = " + JSON.toJSONString(real));
return real;
}
diff --git a/APIJSONORM/src/main/resources/application.properties b/APIJSONORM/src/main/resources/application.properties
deleted file mode 100755
index e69de29bb..000000000
diff --git a/README.md b/README.md
index 99d0f7272..46d3ecda3 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -59,6 +60,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -66,6 +68,7 @@ This source code is licensed under the Apache License Version 2.0
+
@@ -191,7 +194,7 @@ https://github.com/Tencent/APIJSON/wiki
* **解决十大痛点** (可帮前后端开发大幅提振开发效率、强力杜绝联调扯皮、巧妙规避文档缺陷、非常节省流量带宽)
* **开发提速很大** (CRUD 零代码热更新全自动,APIJSONBoot 对比 SSM、SSH 等保守估计可提速 20 倍以上)
* **腾讯官方开源** (使用 GitHub、Gitee、工蜂 等平台的官方账号开源,微信公众号、腾讯云+社区 等官方公告)
-* **社区影响力大** (GitHub 17K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
+* **社区影响力大** (GitHub 18K+ Star 在 400W Java 项目排名前 100,远超 FLAG, BAT 等国内外绝大部分开源项目)
* **各项荣誉成就** (腾讯内外 5 个奖项、腾讯开源前五、腾讯后端 Star 第一、Trending 日周月榜大满贯 等)
* **多样用户案例** (腾讯内有互娱、音乐、微信、云与智慧,外部有华为、华能、百度、快手、中兴、圆通、传音等)
* **适用场景广泛** (社交聊天、阅读资讯、影音娱乐、办公学习 等各种 App、网站、小程序、公众号 等非金融类项目)
@@ -202,7 +205,7 @@ https://github.com/Tencent/APIJSON/wiki
* **灵活定制业务** (在后端编写 远程函数,可以拿到 session、version、当前 JSON 对象 等,然后自定义处理)
* **高质可靠代码** (代码严谨规范,蚂蚁集团源伞 Pinpoint 代码扫描分析报告平均每行代码 Bug 率低至 0.15%)
* **兼容各种项目** (协议不限 HTTP,与其它库无冲突,对各类 Web 框架集成友好且提供 SpringBoot, JFinal 的示例)
-* **工程轻量小巧** (仅依赖 fastjson,Jar 仅 280KB,Java 文件仅 59 个共 13719 行代码,例如 APIJSONORM 4.3.1)
+* **工程轻量小巧** (无第三方依赖,Jar 仅 263KB,Java 文件仅 68 个共 14864 行代码,例如 APIJSONORM 8.1.0)
* **多年持续迭代** (自 2016 年起已连续维护 8 年多,70+ 贡献者、100+ 发版、3000+ 提交,不断更新迭代中...)
**按照一般互联网中小型项目情况可得出以下对比表格:**
@@ -629,6 +632,12 @@ Issue/问卷 一般解答顺序:贡献者> 帮助他人的用户> 提供任
[APIJSON项目实战教程:零代码实现高效JSON接口开发](https://blog.csdn.net/gitblog_00682/article/details/148375065)
+[springboot整合APIJSON——零代码万能通用 API(附源码)](https://blog.csdn.net/longzhutengyue/article/details/150579233)
+
+[APIJSON:重新定义后端开发体验的零代码ORM框架](https://blog.csdn.net/gitblog_01177/article/details/155216163)
+
+[API自动生成这么爽?实测腾讯APIJSON,零代码就能玩转后端接口!](https://mp.weixin.qq.com/s/DmMIGHHcZ783KobGecMxGg)
+
### 生态项目
[APIJSON-Demo](https://github.com/APIJSON/APIJSON-Demo) APIJSON 各种语言、各种框架 的 使用示例项目、上手文档、测试数据 SQL 文件 等
@@ -640,10 +649,12 @@ Issue/问卷 一般解答顺序:贡献者> 帮助他人的用户> 提供任
[apijson-column](https://github.com/APIJSON/apijson-column) APIJSON 的字段插件,支持 字段名映射 和 !key 反选字段
-[apijson-gson](https://github.com/APIJSON/apijson-gson) APIJSON 的 gson 插件,简化使用
+[apijson-jackson](https://github.com/APIJSON/apijson-jackson) APIJSON 的 jackson 插件,简化使用
[apijson-fastjson2](https://github.com/APIJSON/apijson-fastjson2) APIJSON 的 fastjson2 插件,简化使用
+[apijson-gson](https://github.com/APIJSON/apijson-gson) APIJSON 的 gson 插件,简化使用
+
[apijson-milvus](https://github.com/APIJSON/apijson-milvus) APIJSON 的 Milvus AI 向量数据库插件
[apijson-influxdb](https://github.com/APIJSON/apijson-influxdb) APIJSON 的 InfluxDB 物联网时序数据库插件
@@ -684,6 +695,8 @@ Issue/问卷 一般解答顺序:贡献者> 帮助他人的用户> 提供任
[uliweb-apijson](https://github.com/zhangchunlin/uliweb-apijson) Python 版 APIJSON,支持 MySQL, PostgreSQL, SQL Server, Oracle, SQLite 等
+[apijson-rust](https://gitee.com/APIJSON/panda-base) APIJSON 的 Rust 版,一个优雅、高性能的 Rust 多数据源管理系统,支持 MySQL 和 PostgreSQL
+
[APIJSONParser](https://github.com/Zerounary/APIJSONParser) 第三方 APIJSON 解析器,将 JSON 动态解析成 SQL
[FfApiJson](https://gitee.com/own_3_0/ff-api-json) 用 JSON 格式直接生成 SQL,借鉴 APIJSON 支持多数据源