Skip to content

Commit 3a89b60

Browse files
committed
Merge branch 'master' of https://github.com/Tencent/APIJSON
* 'master' of https://github.com/Tencent/APIJSON: (44 commits) 生态周边项目新增 apijson-practice,感谢贡献 1.根据方法不同拼接聚合语句 2.修改Oracle分组统计语句 update a problem about oracle add delete and update Update AbstractObjectParser.java Modified:修复put请求key的bug 使用登记 新增 腾讯科技有限公司 用户登记新增 腾讯科技有限公司 用户登记新增 腾讯科技有限公司 Update README.md commit test 新增包括 1 个腾讯工程师在内的 8 个贡献者,感谢大家的贡献 新增包括 1 个腾讯工程师在内的 8 个贡献者,感谢大家的贡献 新增包括 1 个腾讯工程师在内的 8 个贡献者,感谢大家的贡献 updated users(Tencent) Update users(Tencent) and contributors(from Tencent, Zhihu, YTO Express) update users(companies) and contributors 简介新增 "零代码实时满足千变万化的各种新增和变更需求" Update CONTRIBUTING.md Update CONTRIBUTING.md ...
2 parents ac9dc85 + 7a7eed6 commit 3a89b60

File tree

8 files changed

+199
-46
lines changed

8 files changed

+199
-46
lines changed

APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,8 @@ else if ((method == POST || method == PUT) && value instanceof JSONArray
261261
&& JSONRequest.isTableArray(key)) { // JSONArray,批量新增或修改,往下一级提取
262262
onTableArrayParse(key, (JSONArray) value);
263263
}
264-
else if (method == PUT && value instanceof JSONArray
265-
&& (whereList == null || whereList.contains(key) == false)) { // PUT JSONArray
264+
else if (method == PUT && value instanceof JSONArray && (whereList == null || whereList.contains(key) == false) && StringUtil.isName(key.replaceFirst("[+-]$", "")))
265+
{ // PUT JSONArray
266266
onPUTArrayParse(key, (JSONArray) value);
267267
}
268268
else { // JSONArray或其它Object,直接填充

APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,7 @@
2121
import static apijson.JSONObject.KEY_ROLE;
2222
import static apijson.JSONObject.KEY_SCHEMA;
2323
import static apijson.JSONObject.KEY_USER_ID;
24-
import static apijson.RequestMethod.DELETE;
25-
import static apijson.RequestMethod.GET;
26-
import static apijson.RequestMethod.GETS;
27-
import static apijson.RequestMethod.HEADS;
28-
import static apijson.RequestMethod.POST;
29-
import static apijson.RequestMethod.PUT;
24+
import static apijson.RequestMethod.*;
3025
import static apijson.SQL.AND;
3126
import static apijson.SQL.NOT;
3227
import static apijson.SQL.OR;
@@ -124,6 +119,7 @@ public abstract class AbstractSQLConfig implements SQLConfig {
124119
DATABASE_LIST.add(DATABASE_SQLSERVER);
125120
DATABASE_LIST.add(DATABASE_ORACLE);
126121
DATABASE_LIST.add(DATABASE_DB2);
122+
DATABASE_LIST.add(DATABASE_CLICKHOUSE);
127123

128124

129125
RAW_MAP = new LinkedHashMap<>(); // 保证顺序,避免配置冲突等意外情况
@@ -508,10 +504,17 @@ public boolean isDb2() {
508504
public static boolean isDb2(String db) {
509505
return DATABASE_DB2.equals(db);
510506
}
507+
@Override
508+
public boolean isClickHouse() {
509+
return isClickHouse(getSQLDatabase());
510+
}
511+
public static boolean isClickHouse(String db) {
512+
return DATABASE_CLICKHOUSE.equals(db);
513+
}
511514

512515
@Override
513516
public String getQuote() {
514-
return isMySQL() ? "`" : "\"";
517+
return isMySQL()||isClickHouse() ? "`" : "\"";
515518
}
516519

517520
@Override
@@ -882,7 +885,7 @@ public String getOrderString(boolean hasPrefix) {
882885
// return (hasPrefix ? " ORDER BY " : "") + StringUtil.concat(order, joinOrder, ", ");
883886
// }
884887

885-
if (getCount() > 0 && (isOracle() || isSQLServer() || isDb2())) { // Oracle, SQL Server, DB2 的 OFFSET 必须加 ORDER BY
888+
if (getCount() > 0 && (isSQLServer() || isDb2())) { // Oracle, SQL Server, DB2 的 OFFSET 必须加 ORDER BY.去掉Oracle,Oracle里面没有offset关键字
886889

887890
// String[] ss = StringUtil.split(order);
888891
if (StringUtil.isEmpty(order, true)) { //SQL Server 子查询内必须指定 OFFSET 才能用 ORDER BY
@@ -1171,9 +1174,9 @@ public String getColumnString(boolean inSQLJoin) throws Exception {
11711174
// }
11721175
}
11731176

1174-
if (expression.length() > 50) {
1177+
if (expression.length() > 100) {
11751178
throw new UnsupportedOperationException("@column:value 的 value 中字符串 " + expression + " 不合法!"
1176-
+ "不允许传超过 50 个字符的函数或表达式!请用 @raw 简化传参!");
1179+
+ "不允许传超过 100 个字符的函数或表达式!请用 @raw 简化传参!");
11771180
}
11781181

11791182

@@ -2158,6 +2161,9 @@ public String getRegExpString(String key, String value, boolean ignoreCase) {
21582161
if (isOracle()) {
21592162
return "regexp_like(" + getKey(key) + ", " + getValue(value) + (ignoreCase ? ", 'i'" : ", 'c'") + ")";
21602163
}
2164+
if (isClickHouse()) {
2165+
return "match(" + (ignoreCase ? "lower(" : "") + getKey(key) + (ignoreCase ? ")" : "") + ", " + (ignoreCase ? "lower(" : "") + getValue(value) + (ignoreCase ? ")" : "") + ")";
2166+
}
21612167
return getKey(key) + " REGEXP " + (ignoreCase ? "" : "BINARY ") + getValue(value);
21622168
}
21632169
//~ regexp >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -2448,7 +2454,12 @@ else if (isOracle()) {
24482454
else {
24492455
boolean isNum = c instanceof Number;
24502456
String v = (isNum ? "" : "\"") + childs[i] + (isNum ? "" : "\"");
2451-
condition += ("json_contains(" + getKey(key) + ", " + getValue(v) + ")");
2457+
if (isClickHouse()) {
2458+
condition += condition + "has(JSONExtractArrayRaw(assumeNotNull(" + getKey(key) + "))" + ", " + getValue(v) + ")";
2459+
}
2460+
else {
2461+
condition += ("json_contains(" + getKey(key) + ", " + getValue(v) + ")");
2462+
}
24522463
}
24532464
}
24542465
}
@@ -2571,7 +2582,7 @@ public String getSetString(RequestMethod method, Map<String, Object> content, bo
25712582
if (setString.isEmpty()) {
25722583
throw new IllegalArgumentException("PUT 请求必须在Table内设置要修改的 key:value !");
25732584
}
2574-
return " SET " + setString;
2585+
return (isClickHouse()?" ":" SET ") + setString;
25752586
}
25762587

25772588
/**SET key = concat(key, 'value')
@@ -2649,8 +2660,14 @@ public static String getSQL(AbstractSQLConfig config) throws Exception {
26492660
case POST:
26502661
return "INSERT INTO " + tablePath + config.getColumnString() + " VALUES" + config.getValuesString();
26512662
case PUT:
2663+
if(config.isClickHouse()){
2664+
return "ALTER TABLE " + tablePath + " UPDATE"+ config.getSetString()+ config.getWhereString(true);
2665+
}
26522666
return "UPDATE " + tablePath + config.getSetString() + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : "");
26532667
case DELETE:
2668+
if(config.isClickHouse()){
2669+
return "ALTER TABLE " + tablePath + " DELETE" + config.getWhereString(true);
2670+
}
26542671
return "DELETE FROM " + tablePath + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : ""); // PostgreSQL 不允许 LIMIT
26552672
default:
26562673
String explain = (config.isExplain() ? (config.isSQLServer() || config.isOracle() ? "SET STATISTICS PROFILE ON " : "EXPLAIN ") : "");
@@ -2663,18 +2680,22 @@ public static String getSQL(AbstractSQLConfig config) throws Exception {
26632680
String column = config.getColumnString();
26642681
if (config.isOracle()) {
26652682
//When config's database is oracle,Using subquery since Oracle12 below does not support OFFSET FETCH paging syntax.
2666-
return explain + "SELECT * FROM (SELECT"+ (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + ") " + config.getLimitString();
2683+
//针对oracle分组后条数的统计
2684+
if ((config.getMethod() == HEAD || config.getMethod() == HEADS)
2685+
&& StringUtil.isNotEmpty(config.getGroup(),true)){
2686+
return explain + "SELECT count(*) FROM (SELECT "+ (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + ") " + config.getLimitString();
2687+
}
2688+
return explain + "SELECT * FROM (SELECT "+ (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + ") " + config.getLimitString();
26672689
}
26682690

26692691
return explain + "SELECT " + (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + config.getLimitString();
26702692
}
26712693
}
26722694

26732695
/**获取条件SQL字符串
2674-
* @param page
26752696
* @param column
26762697
* @param table
2677-
* @param where
2698+
* @param config
26782699
* @return
26792700
* @throws Exception
26802701
*/
@@ -2686,11 +2707,21 @@ private static String getConditionString(String column, String table, AbstractSQ
26862707
table = config.getSubqueryString(from) + " AS " + config.getAliasWithQuote() + " ";
26872708
}
26882709

2689-
String condition = table + config.getJoinString() + where + (
2690-
RequestMethod.isGetMethod(config.getMethod(), true) == false ?
2691-
"" : config.getGroupString(true) + config.getHavingString(true) + config.getOrderString(true)
2692-
)
2693-
; //+ config.getLimitString();
2710+
//根据方法不同,聚合语句不同。GROUP BY 和 HAVING 可以加在 HEAD 上, HAVING 可以加在 PUT, DELETE 上,GET 全加,POST 全都不加
2711+
String aggregation = "";
2712+
if (RequestMethod.isGetMethod(config.getMethod(), true)){
2713+
aggregation = config.getGroupString(true) + config.getHavingString(true) +
2714+
config.getOrderString(true);
2715+
}
2716+
if (RequestMethod.isHeadMethod(config.getMethod(), true)){
2717+
aggregation = config.getGroupString(true) + config.getHavingString(true) ;
2718+
}
2719+
if (config.getMethod() == PUT || config.getMethod() == DELETE){
2720+
aggregation = config.getHavingString(true) ;
2721+
}
2722+
2723+
String condition = table + config.getJoinString() + where + aggregation;
2724+
; //+ config.getLimitString();
26942725

26952726
//no need to optimize
26962727
// if (config.getPage() <= 0 || ID.equals(column.trim())) {
@@ -2727,7 +2758,6 @@ private static String getConditionString(String column, String table, AbstractSQ
27272758
// return table + " AS t0 INNER JOIN (SELECT id FROM " + condition + ") AS t1 ON t0.id = t1.id";
27282759
}
27292760

2730-
27312761
private boolean keyPrefix;
27322762
@Override
27332763
public boolean isKeyPrefix() {
@@ -3111,7 +3141,7 @@ else if (w.startsWith("!")) {
31113141
}
31123142

31133143
//解决AccessVerifier新增userId没有作为条件,而是作为内容,导致PUT,DELETE出错
3114-
if (isWhere) {
3144+
if (isWhere || (StringUtil.isName(key) == false)) {
31153145
tableWhere.put(key, value);
31163146
if (whereList == null || whereList.contains(key) == false) {
31173147
andList.add(key);

APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,14 @@ public JSONObject execute(@NotNull SQLConfig config, boolean unknowType) throws
287287
// bugfix-修复非常规数据库字段,获取表名失败导致输出异常
288288
if (isExplain == false && hasJoin && viceColumnStart > length) {
289289
List<String> column = config.getColumn();
290-
290+
String sqlTable = rsmd.getTableName(i);
291+
if (config.isClickHouse()&&(sqlTable.startsWith("`")||sqlTable.startsWith("\""))){
292+
sqlTable = sqlTable.substring(1,sqlTable.length()-1);
293+
}
291294
if (column != null && column.isEmpty() == false) {
292295
viceColumnStart = column.size() + 1;
293296
}
294-
else if (config.getSQLTable().equalsIgnoreCase(rsmd.getTableName(i)) == false) {
297+
else if (config.getSQLTable().equalsIgnoreCase(sqlTable) == false) {
295298
viceColumnStart = i;
296299
}
297300
}

APIJSONORM/src/main/java/apijson/orm/SQLConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public interface SQLConfig {
2222
String DATABASE_SQLSERVER = "SQLSERVER";
2323
String DATABASE_ORACLE = "ORACLE";
2424
String DATABASE_DB2 = "DB2";
25+
String DATABASE_CLICKHOUSE = "CLICKHOUSE";
2526

2627
String SCHEMA_INFORMATION = "information_schema"; //MySQL, PostgreSQL, SQL Server 都有的系统模式
2728
String SCHEMA_SYS = "sys"; //SQL Server 系统模式
@@ -37,6 +38,7 @@ public interface SQLConfig {
3738
boolean isSQLServer();
3839
boolean isOracle();
3940
boolean isDb2();
41+
boolean isClickHouse();
4042
//暂时只兼容以上 5 种
4143
// boolean isSQL();
4244
// boolean isTSQL();

CONTRIBUTING.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,16 @@
3232
- [jun0315](https://github.com/jun0315)(腾讯工程师)
3333
- [JieJo](https://github.com/JieJo)
3434
- [yeyuezhishou](https://github.com/yeyuezhishou)(圆通工程师)
35-
- [kenlig](https://github.com/kenlig)
36-
- [andream7](https://github.com/andream7)
37-
35+
- [kenlig](https://github.com/kenlig)(还开源了 apijsondocs)
36+
- [andream7](https://github.com/andream7)(还开源了 apijson-db2)
37+
- [qiujunlin](https://github.com/qiujunlin)(还开源了 APIJSONDemo)
38+
- [HANXU2018](https://github.com/HANXU2018)(还开源了 APIJSON-DOC)
39+
- [hclown9804](https://github.com/hclown9804)
40+
- [chenyanlann](https://github.com/chenyanlann)(还开源了 APIJSONDemo_ClickHouse)
41+
- [haolingzhang1](https://github.com/haolingzhang1)(腾讯工程师,还开源了 APIJson--demo)
42+
- [jerrylususu](https://github.com/jerrylususu)(还开源了 apijson_todo_demo 和 apijson_role_extend)
43+
- [Dalezee](https://github.com/Dalezee)(还开源了 apijson_camp)
44+
- [aaronlinv](https://github.com/aaronlinv)
3845

3946
#### 其中特别致谢: <br/>
4047
justinfengchen 提交的 6 个 Commits, 对 APIJSON 做出了 3,130 增加和 0 处删减(截止 2020/11/04 日); <br/>
@@ -74,6 +81,19 @@ APIJSON 作为腾讯开源的知名热门项目,贡献代码除了可以给简
7481

7582
我们除了希望听到您的反馈和建议外,我们也希望您接受代码形式的直接帮助,对我们的 GitHub 发出 Pull Request 请求。
7683

84+
### 如果是小改文档或代码
85+
86+
直接点文件右上角的编辑图标按钮 <br/>
87+
![image](https://user-images.githubusercontent.com/5738175/130585672-8bd49ae5-2978-4ad6-a7a6-de0a0c2d0b68.png)
88+
89+
<br/>
90+
91+
然后底部简要输入修改说明,点击 Commit Change 按钮 <br/>
92+
![image](https://user-images.githubusercontent.com/5738175/130586073-4a6aea74-3c88-4cd9-9c93-ffaba1270ab8.png)
93+
94+
95+
### 如果有比较大的改动
96+
7797
以下是具体步骤:(如果使用本步骤,GitHub 可能不会把贡献者添加到 Contributors 中,推荐用以下 [详细的图文步骤](https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md#%E8%AF%A6%E7%BB%86%E7%9A%84%E5%9B%BE%E6%96%87%E6%AD%A5%E9%AA%A4%E5%8F%AF%E5%8F%82%E8%80%83%E4%BB%A5%E4%B8%8B%E4%BB%BB%E6%84%8F%E4%B8%80%E7%AF%87))
7898

7999
#### Fork 仓库
@@ -100,8 +120,11 @@ $ git remote add APIJSON git@github.com:Tencent/APIJSON.git
100120

101121
#### 保持与 APIJSON 仓库的同步
102122

103-
更新上游仓库:
123+
直接在 fork Repo 的首页点 Contribute > Open pull request
124+
125+
![image](https://user-images.githubusercontent.com/5738175/131776033-74caf279-ebbf-45f1-a9c1-beff937a87fb.png)
104126

127+
或者
105128
```bash
106129
$ git pull --rebase <name> <branch>
107130
# 等同于以下两条命令

Document.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# APIJSON通用文档
2-
后端开发者可以先看这个详细的 [图文入门教程](https://vincentcheng.github.io/apijson-doc/zh)(如果和本文档有出入,以本文档为准,例如正则匹配 key? 已废弃,全面用 key~ 替代)
2+
后端开发者可以先看这个详细的 [图文入门教程](https://vincentcheng.github.io/apijson-doc/zh)(如果和本文档有出入,以本文档为准,例如正则匹配 key? 已废弃,全面用 key~ 替代
33

44
* ### [1.示例](#1)
55
* ### [2.对比传统方式](#2)

README-English.md

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,13 @@ If you have any questions or suggestions, you can [create an issue](https://gith
307307

308308
### Users of this project:
309309

310+
https://github.com/Tencent/APIJSON/issues/187
310311
<div style="float:left">
312+
<a href="https://ieg.tencent.com"><img src="https://user-images.githubusercontent.com/5738175/126525534-461c3e33-57b1-4630-af7f-f1238ca4ab98.png" height="75"></a>
313+
<a href="https://cloud.tencent.com"><img src="https://user-images.githubusercontent.com/5738175/126525251-c05e64c6-6b60-4457-a46e-dea7dcfb80cd.png" height="75"></a>
314+
<a href="https://www.tencentmusic.com"><img src="https://user-images.githubusercontent.com/5738175/126524713-b38a8f02-3086-45d4-91db-6f5389811d51.png" height="75"></a>
315+
<br />
316+
311317
<a href="http://www.transsion.com"><img src="http://apijson.cn/images/www.transsion.com.jpeg" height="75"></a>
312318
<a href="http://shebaochina.com"><img src="http://apijson.cn/images/shebaochina.com.png" height="75"></a>
313319
<a href="http://www.xmfish.com"><img src="http://apijson.cn/images/www.xmfish.com.gif" height="75"></a>
@@ -325,7 +331,8 @@ If you have any questions or suggestions, you can [create an issue](https://gith
325331
[More APIJSON Users](https://github.com/Tencent/APIJSON/issues/73)
326332

327333
### Contributers of APIJSON:
328-
Here are the contributers of this project and authors of other projects for ecosystem of APIJSON:
334+
Contributers for the APIJSON core project(6 Tencent engineers、1 Zhihu architect、1 YTO Express engineer, etc.): <br />
335+
https://github.com/Tencent/APIJSON/blob/master/CONTRIBUTING.md <br />
329336
<div style="float:left">
330337
<a href="https://github.com/TommyLemon"><img src="https://avatars1.githubusercontent.com/u/5738175?s=400&u=5b2f372f0c03fae8f249d2d754e38971c2e17b92&v=4"
331338
height="54" width="54" ></a>
@@ -350,11 +357,23 @@ Here are the contributers of this project and authors of other projects for ecos
350357
<a href="https://github.com/Tencent/APIJSON/pull/33"><img src="https://avatars1.githubusercontent.com/u/5328313?s=460&v=4" height="54" width="54" ></a>
351358
<a href="https://github.com/Tencent/APIJSON/pull/235"><img src="https://avatars.githubusercontent.com/u/17243165?v=4" height="54" width="54" ></a>
352359
<a href="https://github.com/Tencent/APIJSON/pull/233"><img src="https://avatars.githubusercontent.com/u/1252459?v=4" height="54" width="54" ></a>
353-
354-
<br />
360+
<a href="https://github.com/Tencent/APIJSON/pull/250"><img src="https://avatars.githubusercontent.com/u/44310040?v=4" height="54" width="54" ></a>
361+
<a href="https://github.com/Tencent/APIJSON/pull/253"><img src="https://avatars.githubusercontent.com/u/19265050?v=4" height="54" width="54" ></a>
362+
<a href="https://github.com/Tencent/APIJSON/pull/278"><img src="https://avatars.githubusercontent.com/u/4099373?v=4" height="54" width="54" ></a>
363+
<a href="https://github.com/Tencent/APIJSON/pull/279"><img src="https://avatars.githubusercontent.com/u/28685375?v=4" height="54" width="54" ></a>
364+
<a href="https://github.com/Tencent/APIJSON/pull/280"><img src="https://avatars.githubusercontent.com/u/60541766?v=4" height="54" width="54" ></a>
365+
</div>
366+
<br />
367+
368+
Authors of other projects for ecosystem of APIJSON(2 Tencent engineers、1 Bytedance(TikTok) engineer, etc.): <br />
369+
https://github.com/search?o=desc&q=apijson&s=stars&type=Repositories <br />
370+
https://search.gitee.com/?skin=rec&type=repository&q=apijson&sort=stars_count <br />
371+
<div style="float:left">
355372
<a href="https://github.com/APIJSON/apijson-orm"><img src="https://avatars.githubusercontent.com/u/41146037?s=200&v=4"
356373
height="54" width="54" ></a>
357374
<a href="https://github.com/liaozb/APIJSON.NET"><img src="https://avatars3.githubusercontent.com/u/12622501?s=400&v=4"
375+
height="54" width="54" ></a>
376+
<a href="https://gitee.com/tiangao/apijson-go"><img src="https://portrait.gitee.com/uploads/avatars/user/43/130007_tiangao_1578918889.png!avatar200"
358377
height="54" width="54" ></a>
359378
<a href="https://github.com/qq547057827/apijson-php"><img src="https://avatars3.githubusercontent.com/u/1657532?s=400&v=4" height="54" width="54" ></a>
360379
<a href="https://github.com/xianglong111/json-api"><img src="https://avatars.githubusercontent.com/u/9738743?s=460&v=4" height="54" width="54" ></a>
@@ -383,8 +402,12 @@ Here are the contributers of this project and authors of other projects for ecos
383402
<a href="https://github.com/pengxianggui/apijson-builder"><img src="https://avatars2.githubusercontent.com/u/16299169?s=460&v=4" height="54" width="54" ></a>
384403
<a href="https://github.com/APIJSON/AbsGrade"><img src="https://avatars.githubusercontent.com/u/41146037?s=200&v=4" height="54" width="54" ></a>
385404
<a href="https://github.com/TommyLemon/Android-ZBLibrary"><img src="https://avatars1.githubusercontent.com/u/5738175?s=400&u=5b2f372f0c03fae8f249d2d754e38971c2e17b92&v=4" height="54" width="54" ></a>
405+
<a href="https://github.com/APIJSON/apijson_todo_demo"><img src="https://avatars.githubusercontent.com/u/17522475?v=4" height="54" width="54" ></a>
386406
</div>
387407
<br />
388-
Thanks to all contributers of APIJSON!
389408

409+
410+
<br />
411+
Thanks to all contributers of APIJSON!
412+
390413
<br />

0 commit comments

Comments
 (0)