From 96725dea2f73c0d77543ebf7df1f215ade7c9d40 Mon Sep 17 00:00:00 2001 From: Sea Date: Mon, 24 Dec 2018 16:07:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=BA=9BBUG,?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E8=A1=A8=E5=92=8C=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E7=9A=84=E9=BB=91=E7=99=BD=E5=90=8D=E5=8D=95=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 44 +++ .../biao/apijson/parser/APIJSONProvider.java | 275 +++++++++++++----- .../java/zuo/biao/apijson/parser/App.java | 21 +- .../apijson/parser/SQLProviderException.java | 20 ++ .../APIJSONParser/pom.properties | 2 +- .../biao/apijson/parser/APIJSONProvider.class | Bin 18064 -> 21193 bytes .../classes/zuo/biao/apijson/parser/App.class | Bin 2073 -> 2171 bytes .../apijson/parser/SQLProviderException.class | Bin 2425 -> 3337 bytes 8 files changed, 287 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index d5300c3..3ecc3eb 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,47 @@ 详细文档,计划2019年1月1日左右更新 +## 未来 + +1. 现在的程序是写死的,处理逻辑能以链的形式组合就好了 + +@pipe("name") + +如果能够自动装配的话,最好有一个 + +2. 使用Mybatis别名的技巧来实现自动装配 + +```json +{ + "T":{ + "@column":"a,b,c" + }, + "T2":{ + "@parent":"T" + "@column":"d,e,f" + } +} +``` + +查询结果: + +```json + { + "T":{ + "a":"a", + "b":"b", + "c":"c", + "T2":{ + "d":"d", + "e":"e", + "f":"f" + } + } + } +``` + +3. 表,字段的黑白名单放出来,作为 + +4. 权限的控制通过 + + \ No newline at end of file diff --git a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java index 6268cd9..18bb3ac 100644 --- a/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java +++ b/src/main/java/zuo/biao/apijson/parser/APIJSONProvider.java @@ -12,6 +12,11 @@ public class APIJSONProvider extends AbstractProvider { private final String ALIAS_SPLIT = ":"; private static final String AND = " AND "; private static final String OR = " OR "; + + private List tableWhiteList = new ArrayList(); + private List tableBlackList = new ArrayList(); + private List columnWhiteList = new ArrayList(); + private List columnBlackList = new ArrayList(); private JSONObject request; private JSONObject join; @@ -44,6 +49,10 @@ public APIJSONProvider(JSONObject obj) { /** * 解析请求中的表名 * 表名必须符合:(\w+(:\w+)?) + * 即: + * 表名 + * 表名:表别名 + * 两种形式 */ @Override public List getTables() { @@ -65,7 +74,7 @@ public List getTables() { }else { tableRealName = tableAliasName = tableName; } - + validateTable(tableRealName); list.add(tableRealName + " " + tableAliasName); } return list; @@ -87,7 +96,7 @@ public List getTables() { * 支持函数的正则:((\w+\(\w+\):\w+|\w+)(:\w+)?)+(\s?,\s?((\w+\(\w+\):\w+|\w+)(:\w+)?)+)* * 例:a,b,c或a:a1,b:b1,c * 约束:必须要有表名 - * 目前不支持函数 + * 目前支持函数 */ @Override public List getSelect() { @@ -95,6 +104,7 @@ public List getSelect() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { Set tableNames = request.keySet(); + String tableRealName; String tableAliasName; for(String tableName : tableNames) { //格式检查 @@ -104,16 +114,18 @@ public List getSelect() { } //是否有自定义别名 if(tableName.contains(ALIAS_SPLIT)) { + tableRealName = tableName.split(ALIAS_SPLIT)[0]; //填写了表别名 tableAliasName = tableName.split(ALIAS_SPLIT)[1]; }else { - tableAliasName = tableName; + tableRealName = tableAliasName = tableName; } // 获取请求@column的值 JSONObject propertis = request.getJSONObject(tableName); String columnsValue = propertis.getString("@column"); if(columnsValue == null) { + validateColumn(tableRealName,"*"); //没有填写@column字段,默认为全部 list.add(tableAliasName + ".*"); }else { @@ -138,15 +150,18 @@ public List getSelect() { //此处省略函数合法性检查的代码... String columnRealName = functionStrs[1]; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; + validateColumn(tableRealName,columnRealName); list.add(funcitonName + "(" + tableAliasName + "." + columnRealName + ")" + " as " + columnAliasName); }else { //没函数的字段 String columnRealName = functionOrColumn ; String columnAliasName = columnName.split(ALIAS_SPLIT)[1]; + validateColumn(tableRealName,columnRealName); list.add(tableAliasName + "." + columnRealName + " as " + columnAliasName); } }else { //使用tableAliasName.columnName类型 + validateColumn(tableRealName,columnName); list.add(tableAliasName + "." + columnName); } } @@ -359,7 +374,7 @@ public List getWhere() { } /** * 内连接 - * 请求:table1.column1 = table2.column2 + * 请求:"@innerJoin" : ["table1.column1 = table2.column2","table1.column1 = table2.column2"] * 编译之后:INNER JOIN table1 ON table1.column1=table2.column2 */ @Override @@ -368,22 +383,26 @@ public List getInnerJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@innerJoin")!=null) { - Object obj = join.get("@innerJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@innerJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@innerJoin的格式必须是:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@innerJoin的格式必须是:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2"); + error("@innerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@innerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于INNER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -399,22 +418,26 @@ public List getLeftOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@leftOuterJoin")!=null) { - Object obj = join.get("@leftOuterJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@leftOuterJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@leftOuterJoin的格式必须是:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@leftOuterJoin的格式必须是:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@leftOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@leftOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于LEFT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -429,22 +452,26 @@ public List getRightOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@rightOuterJoin")!=null) { - Object obj = join.get("@rightOuterJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@rightOuterJoin"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@rightOuterJoin的格式必须是:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@rightOuterJoin的格式必须是:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@rightOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@rightOuterJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于RIGHT OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -459,22 +486,26 @@ public List getJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@join")!=null) { - Object obj = join.get("@join"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@join"); + for(int i = 0; i < stms.size(); i++) { + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@join的格式必须是:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@join的格式必须是:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2"); + error("@join的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@join的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -489,22 +520,27 @@ public List getOuterJoin() { List list = new ArrayList<>(); if(getStatementType() == StatementType.SELECT) { if(join != null && join.get("@outerJoin")!=null) { - Object obj = join.get("@outerJoin"); - if(obj instanceof String) { - String joinStr = (String)obj; - if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { - //table1.column1 = table2.column2 - String[] tcs = joinStr.replaceAll("\\s", "").split("="); - String leftTable = tcs[0].split("\\.")[0]; - String rightTable = tcs[1].split("\\.")[0]; - list.add(leftTable + " ON " + joinStr); + JSONArray stms = join.getJSONArray("@join"); + for(int i = 0; i < stms.size(); i++) { + + Object obj = stms.get(i); + if(obj instanceof String) { + String joinStr = (String)obj; + if(joinStr.matches("\\w+\\.\\w+\\s?=\\s?\\w+\\.\\w+")) { + //table1.column1 = table2.column2 + String[] tcs = joinStr.replaceAll("\\s", "").split("="); + String leftTable = tcs[0].split("\\.")[0]; + String rightTable = tcs[1].split("\\.")[0]; + validateTable(leftTable); + list.add(leftTable + " ON " + joinStr); + }else { + error("@outerJoin的格式必须是:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2"); + return null; + } }else { - error("@outerJoin的格式必须是:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2"); + error("@outerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); return null; } - }else { - error("@outerJoin的类型必须是String类型,填写的值如:table1.column1 = table2.column2,相当于OUTER JOIN table1 ON table1.column1=table2.column2。注意:表有别名的应该写表别名"); - return null; } } } @@ -630,6 +666,7 @@ public List getColumns() { continue; if(condition.matches("\\w+")) { //纯字段名 + validateColumn(tableName,condition); list.add(condition); }else { error("新增时,"+condition+"必须是字段名"); @@ -724,8 +761,10 @@ public List getSets() { propertis.get(condition) instanceof Float || propertis.get(condition) instanceof Double || propertis.get(condition) instanceof BigDecimal) { + validateColumn(tableName,columnName); list.add(tableName + "." + columnName + "=" + propertis.get(condition).toString() ); }else if(propertis.get(condition) instanceof String) { + validateColumn(tableName, columnName); list.add(tableName + "." + columnName + "=" + "'" + ((String) propertis.get(condition)).replaceAll("'", "''") + "'"); } } @@ -735,4 +774,102 @@ public List getSets() { } return list; } + + /* + * ================================== + * 权限逻辑 + * ================================== + * + * 不管权限认证系统有多复杂,最后到生成SQL这步 + * 都是进行黑白名单的检查 + * 不论黑白,只要名单为空,表示所有数据都可以 + * 表名单: + * 格式:表名 + * 大小写不敏感 + * 字段名单: + * 格式:表名.字段 + * 大小写不敏感 + * 所有字段: + * 格式: 表名.* + * + * 如果想要新增或者修改的更复杂的逻辑, + * 请在外层处理完成之后以制定格式提交该SQL的黑白名单即可 + * + */ + /** + * 表的黑白名单检查 + * @param tableName + */ + private void validateTable(String tableName) { + String tableUCN = tableName.toUpperCase(); + if(tableBlackList != null && !tableBlackList.isEmpty()) { + for(String tn : tableBlackList) { + if(tableUCN.equals(tn.toUpperCase())) { + error("请求的表:" + tableName + "在黑名单中"); + return; + } + } + } + + if(tableWhiteList != null && !tableWhiteList.isEmpty()) { + for(String tn : tableWhiteList) { + if(tableUCN.equals(tn.toUpperCase())) { + return; + } + } + error("请求的表:" + tableName + "不在白名单中"); + } + } + /** + * 字段黑白名单检查 + * 要求: "表.列" + * @param columnName + */ + private void validateColumn(String tableName, String columnName) { + String tableUCN = tableName.toUpperCase(); + String columnUCN = columnName.toUpperCase(); + String tcUCN = tableUCN + "." + columnUCN; + if(columnBlackList != null && !columnBlackList.isEmpty()) { + for(String tc : columnBlackList) { + if(tcUCN.equals(tc.toUpperCase())) { + error("请求的字段:" + tcUCN + "在黑名单中"); + return; + } + } + } + + if(columnWhiteList != null && !columnWhiteList.isEmpty()) { + for(String tc : columnWhiteList) { + if(tc.endsWith(".*") || tcUCN.equals(tc.toUpperCase())) { + return; + } + } + error("请求的字段:" + tcUCN + "不在白名单中"); + } + } + + public List getTableWhiteList() { + return tableWhiteList; + } + public void setTableWhiteList(List tableWhiteList) { + this.tableWhiteList = tableWhiteList; + } + public List getTableBlackList() { + return tableBlackList; + } + public void setTableBlackList(List tableBlackList) { + this.tableBlackList = tableBlackList; + } + public List getColumnWhiteList() { + return columnWhiteList; + } + public void setColumnWhiteList(List columnWhiteList) { + this.columnWhiteList = columnWhiteList; + } + public List getColumnBlackList() { + return columnBlackList; + } + public void setColumnBlackList(List columnBlackList) { + this.columnBlackList = columnBlackList; + } } diff --git a/src/main/java/zuo/biao/apijson/parser/App.java b/src/main/java/zuo/biao/apijson/parser/App.java index 2688c5f..d623df2 100644 --- a/src/main/java/zuo/biao/apijson/parser/App.java +++ b/src/main/java/zuo/biao/apijson/parser/App.java @@ -1,5 +1,7 @@ package zuo.biao.apijson.parser; +import java.util.Set; + import com.alibaba.fastjson.JSONObject; /** @@ -12,11 +14,11 @@ public static void main( String[] args ) throws SQLProviderException { long st = System.currentTimeMillis(); String json = "{\r\n" + - " \"Retail\": {\r\n" + - " \"description\": \"20190101,元旦快乐\",\r\n" + - " \"INVOICE_ADDRESS\": 13,\r\n" + - " \"docno\": \"RE1807250000002\"\r\n" + - " }\r\n" + + " \"Retail\":{\r\n" + + " \"id\": 1,\r\n" + + " \"amt\": 1,\r\n" + + " \"@column\": \"id,cc\"\r\n" + + " }\r\n" + "}"; JSONObject req = JSONObject.parseObject(json); long fastJsonet = System.currentTimeMillis(); @@ -24,6 +26,14 @@ public static void main( String[] args ) throws SQLProviderException long st2 = System.currentTimeMillis(); APIJSONProvider apijsonProvider = new APIJSONProvider(req); apijsonProvider.setStatementType(StatementType.SELECT); +// apijsonProvider.getTableBlackList().add("Retail"); +// apijsonProvider.getTableWhiteList().add("Retail"); +// apijsonProvider.getTableWhiteList().add("StorE"); +// apijsonProvider.getColumnBlackList().add("retail.id"); + apijsonProvider.getColumnWhiteList().add("retail.*"); +// apijsonProvider.getColumnWhiteList().add("retail.amt"); +// apijsonProvider.getColumnBlackList().add("retail.amt"); +// apijsonProvider.getColumnWhiteList().add("store.id"); SQLExplorer builder = new SQLExplorer(apijsonProvider); System.out.println(builder.getSQL()); long et = System.currentTimeMillis(); @@ -32,5 +42,6 @@ public static void main( String[] args ) throws SQLProviderException System.out.println("APIJSONParser解析用时:" + (et - st2) + "ms"); System.out.println("合计用时:" + (et - st) + "ms"); //实际解析耗时21ms + } } diff --git a/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java b/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java index 3b0fff7..12c826e 100644 --- a/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java +++ b/src/main/java/zuo/biao/apijson/parser/SQLProviderException.java @@ -1,7 +1,9 @@ package zuo.biao.apijson.parser; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import zuo.biao.apijson.parser.Message.ErrorElement; @@ -9,6 +11,8 @@ public class SQLProviderException extends Exception { private List errors; + private Set errorMsgs = new HashSet<>(); + public SQLProviderException() { } @@ -22,6 +26,7 @@ public SQLProviderException(List errors) { List stackTrace = new ArrayList<>(); StringBuffer sb = new StringBuffer(); for(int i = 0; i < errors.size(); i++) { + errorMsgs.add(errors.get(i).getErrorMsg() + Utils.NEW_LINE); sb.append( i + ".Error message: ----> " + errors.get(i).getErrorMsg() + Utils.NEW_LINE); stackTrace.add(errors.get(i).getStack()); } @@ -30,4 +35,19 @@ public SQLProviderException(List errors) { super.setStackTrace(stackTrace.toArray(stack)); this.errors = errors; } + + public List getErrors() { + return errors; + } + public void setErrors(List errors) { + this.errors = errors; + } + public Set getErrorMsgs() { + return errorMsgs; + } + public void setErrorMsgs(Set errorMsgs) { + this.errorMsgs = errorMsgs; + } + + } diff --git a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties index 2f5b31f..1f4c4e8 100644 --- a/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties +++ b/target/classes/META-INF/maven/APIJSONParser/APIJSONParser/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven Integration for Eclipse -#Tue Dec 18 10:59:37 CST 2018 +#Mon Dec 24 14:28:09 CST 2018 version=0.0.1-SNAPSHOT groupId=APIJSONParser m2e.projectName=APIJSONParser diff --git a/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class b/target/classes/zuo/biao/apijson/parser/APIJSONProvider.class index 37f6639949ef16c916d45f77ae5533c07502fa6b..6af2e6f9505f8468e358444aff9a9f1fd8657372 100644 GIT binary patch literal 21193 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 L{TfhF_+S48<4y$ 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 diff --git a/target/classes/zuo/biao/apijson/parser/App.class b/target/classes/zuo/biao/apijson/parser/App.class index 0b1fdde302dc5d8891f2bd5af27961017439b109..9c7cd27d4f7bd868dda1a83c2c4621f0b06ef531 100644 GIT binary patch delta 753 zcmZ`$OK%cU6#gy@49pBlrV*t!ff65pO|h+trBy3bZ7Y^SRZ6XoDU5U|52XW|xL|GK z%84;9+_-UNVlA1fO}cUC&b|M{#CV3rs0%me-tTe0bMC#byg$6c*Powv0gU1CrjTN@ z=_ei`7!2i9UD2wcL=O?xN})t>WXRG8DK-6XI8|&^TeUh-JyP=-iC8V#vXP};(`PRri_j~=1fXRVcO{0gT!B! za0N5QcYA1X4rv}&8TxvCv_@>9snzvGy{RfS3D+=h1h^yO92P9#^h?zzuUw~`P3(zu_3>M$%5a#jq?fN11|an6PHZPenekf zBy1u>bqCpkyts`UK1V<`k(Y0oxNYJd8GoxqOBH?me`K{M?;Y;^IlW0U*&t9&!b^2O zI9hoeq-hU9MnCNiBZL7&>G$I}O1@+CWXF+17)ywtP093h5km8~#_k0kq(NOv|LuLD35yyC7mWWdZoH7kdzdiY|n)4{`c{VeF?{_h1YmnCQh5 z^kE$b;34&ONcOdGjWUqa8gAewIPzP?G;YC3t}ju08}RqGPAurwrbH=(7zwe|hh zs=qq^;7>Z*u{>mS{LLB1{zcAMzk7ER@}gsbN%r2o=R4my-#O><*T2914&VsB*5DHe zUrQ$NX2HEJePRwAy!FR*FWm^UIZBR(C; zz7QxkZ98E*0yTr`eL7{uB7>Hb>Jq3NwWi}nDs7tr#|mC|53WaG$aEZI+T7nSf%{|T zoEcAb^+-77YRH+Uw~aY)qo#DWFMp@oT`IbxDcg!qD~$4PD{iHF1j?F2;{yJ^#FQCC z8ESOs*r1_Kpmr^elZG=x^dM?cQwf0v9hC@5?^fI#PR~u6_LwmlV|aCgiKr19H*8C; zy-j~=#v<4Dbwe$cYlhO$TueSRE@|$NG`9(8b8eEP<26~lI0d%k0*uYri7S#-my5AS zV(bJ2#vYiFsq2ePK9%vfLH?tKdx8rSeZrkoV`aoaW)WX%L|5H%WF1U45& z)tkO_$+UwA$pdWyjl~D77;Cl)5ggL+pg{GSjuKnPVH~0E(`HI$cp#J$_UXx4Gn(qs z@X$RPqf}i6(Se6`Jc36Bs)(#=mK7>c-<<2fQznQmJf@+W-BL(jM-LvSu=7SNZJxd) za4=Uxk6=m2Fm_a;7f)#D6WF<4SDj^FIy(BXOQ2%7f8yfcz;J&MVLYkhBnH^XMlxx} zrv%z^9ec!!9rvbHy1OLADIJ3{0~M)+d%g<87}0QAV8faSY$P3L@H90rrsyZH$}<6; zmnj;P$v9i2lE*JgZ=sbn|pK-{#Vi+BGR>yOAo?%eP z(UcLrEU>#kMQ)DSM%2uVC0T7F^25+Ei6{q(V_joUak>mt!o;)&N|`J6f|D}mbj-lw zDGs!3|5=I12*(sX+omx`LLrF|q?K|BlEw%xEjG zoDGWTkdgEzdS#}^Io9hkwUNc6L2uFKea}Z;bj+P9Z+g))9^HGdHV>aIMV|)>_wwV) z-hFxo)`hCeo#YZpj`z&eQ896|B$PZD98IL{sCmMYx8K%M7g@V3Mg}ktkDGR1%y1mj z(QuPZwcd>KCM<1K^qvsd!@?-zzb-!hvO2iZ)T)AOUdP#W6)O15Yn-3eJkd)l!S9-u zC2VY2MeVsIY`Ts5pOy7yepV~o8uSauZHt&) z#O1Ks`VGlWe@l2Px!97+{1UFVwBE+cQc6Fhl$|n2O6E$rjp^CW?C!t;W_yJBex0kE zTz|)M`T={f%tZZ({rHV;cbBdL8ia<3`U|l*|AEb;@8I36TJNbqBHWd=8=w#` z|HKEMic&HHniXTWPy^XQeV@_fNzS-s%K+usT$L%5pxSLE&#*#rd6#EnFZW`XF}Ysk zj*Lm*1ALhE@=+1h`a-JhC8>UlPqHANDv&&?szYrizkKrYovRFS9;pQ*Hc QjxYEvr$h(7#8(ji0YUK+&Hw-a delta 1112 zcmZ{i+iz4=6vlu1%(>0Xp|nFgowjt^YSA(?bp$HyppF-)SSYOuQj3>5YN>;5K{_o` zl|l4{n2=0%0*Noi7fpN;5@)Pc1Fv5G7N3oYW$n|(1ft2wI&1I!t#7UG+xu$gUy0^l z|M~G3U>{WvOOdP9Qni6Ew5w8Tn;un{-PV983H_TYx*aBM*W0a2sz+b5QqeTqJRVRa z^&L0q$LKTbUHT8JGZW)sc6$scn$MJ0O8NQH;#~fC<$QT@E+9iz;jNVBmuDwW>zj5; zYWr#Rc|`wiclrH18nB1GI&BxyafaCM@tC41BwDVN=kw#`rAojsBif1d+6Th9bFml& zo(Oo7qMnKDibr{xXFZ-#bc-sz5%C5Ol2rH;qf;~EV-usM>1e=n6lJq=c5^zT;(614 zO#f-`@e3Rc80VNCaPs~TlL0UAqP}XUhs|Ke15WU=!d@z`&MG1UyT_tTG41iny}~ap zRc03gPVuU)Ilb{dr1=}X=`o{!a#AfN5(?*>xKr_XThXNpPFKuym<>41oQ}D}c3F|u zpSWq!8R5Nvc^347JEG3&uiY>8q(5SRtmvuLdi1w`s;aPXa1o>Bel2`#$)m(1#={#| zl7W+H5ZnwQO%h`Qg+RbfY!9;#L^Cb2G6ab&f}$V|*3D!a?6{wy+AQ5$LW<-jY>Pq) zXd@{khOGU+$Wr%{?GUck|KaMSYl~~A^x4$fO}r|@o)Q&~OnAdcTqixB zBC_B9hNFY)^iQ|hKk!h4Tw<_JKHFxmvCrZ*g*uPR#x0(bSm#ik!&A9?j*qM{VKMcY zt=jDD8ZRln+W77kCxxf_TAkN(;oi@@b->Q#I)wKf!~1T7Gnwr7EE<`{XV@!_BevLL zOdzJTlA@Cij!2x5e1V-@pqneCsnNrAdP6&kVy5W(*V-FfS01qXIp^fi5~@#mpY!r$ z+BwM*6=`xsS}jwSY(>g9@5hw-;$(oSydaey$XN&0hRoo2D377=>yroZH-`mqQ2-XJ Od?