APIJSON(六AbstractObjectParser源码阅读(3))

APIJSON(六:AbstractObjectParser源码阅读(3))

2021SC@SDUSC

parse

if (isReuse == false)

Entry<String, Object>

for (Entry<String, Object> entry : set) {
   if (isBreakParse()) {
      break;
   }

   value = entry.getValue();
   if (value == null) {
      continue;
   }
   key = entry.getKey();

   try {
      if (key.startsWith("@") || key.endsWith("@")) {
         if (onParse(key, value) == false) {
            invalidate();
         }
      }
      else if (value instanceof JSONObject) {  
         if (childMap != null) {  
            childMap.put(key, (JSONObject)value);
         }
         else {  // 直接解析并替换原来的,[]:{} 内必须直接解析,否则会因为丢掉count等属性,并且total@:"/[]/total"必须在[]:{} 后!
            response.put(key, onChildParse(index, key, (JSONObject)value));
            index ++;
         }
      }
      else if ((method == POST || method == PUT) && value instanceof JSONArray
            && JSONRequest.isTableArray(key)) {  // JSONArray,批量新增或修改,往下一级提取
         onTableArrayParse(key, (JSONArray) value);
      }
      else if (method == PUT && value instanceof JSONArray && (whereList == null || whereList.contains(key) == false)
            && StringUtil.isName(key.replaceFirst("[+-]$", ""))) {  // PUT JSONArray
         onPUTArrayParse(key, (JSONArray) value);
      }
      else {  // JSONArray或其它Object,直接填充
         if (onParse(key, value) == false) {
            invalidate();
         }
      }
   } catch (Exception e) {
      if (tri == false) {
         throw e;  // 不忽略错误,抛异常
      }
      invalidate();  // 忽略错误,还原request
   }
}

这里主要是对set内的值进行处理

首先,如果出现BreakParse的情况会直接中断,而根据上篇报告的分析,也就是说——

不能出现invalidate()函数被执行打的情况,不然在下次循环开始就会跳出。

同时,value为空(null)的时候,就会直接跳过该value,进入下一次循环。

首先会对key值进行判断,如果出现@开头或结尾,则会进行onParse函数,如果执行结果为false,则直接判定为invalidate()。原因可能是因为"@“是:局部变量声明,如果没有”@"的字段代表是列名; 所以key中禁止出现@

之后会对value的类型进行判断,如果是JSONOBJECT的类型,就会往下一级提取——添加到childMap,最后再解析。(这里其实还进行了一步childMap有没有创建的判断,如果没有,就会进入onChildParse函数,这个在之后分析)

instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

之后是对method进行判断,如果满足一系列条件,就会触发批量新增或修改,往下一级onTableArrayParse或onPUTArrayParse提取。

最后是如果method方法不为上述的情况,value的值为JSONArray或其它Object,则会进入onParse函数直接填充。

扫描二维码关注公众号,回复: 13306122 查看本文章

同时注意到这里有个tri变量,用于在发生错误的时候是否选择忽略。

if (isTable)

if (isTable) {
   if (parser.getGlobleDatabase() != null && sqlRequest.get(JSONRequest.KEY_DATABASE) == null) {
      sqlRequest.put(JSONRequest.KEY_DATABASE, parser.getGlobleDatabase());
   }
   if (parser.getGlobleSchema() != null && sqlRequest.get(JSONRequest.KEY_SCHEMA) == null) {
      sqlRequest.put(JSONRequest.KEY_SCHEMA, parser.getGlobleSchema());
   }
   if (parser.getGlobleDatasource() != null && sqlRequest.get(JSONRequest.KEY_DATASOURCE) == null) {
      sqlRequest.put(JSONRequest.KEY_DATASOURCE, parser.getGlobleDatasource());
   }

   if (isSubquery == false) {  //解决 SQL 语法报错,子查询不能 EXPLAIN
      if (parser.getGlobleExplain() != null && sqlRequest.get(JSONRequest.KEY_EXPLAIN) == null) {
         sqlRequest.put(JSONRequest.KEY_EXPLAIN, parser.getGlobleExplain());
      }
      if (parser.getGlobleCache() != null && sqlRequest.get(JSONRequest.KEY_CACHE) == null) {
         sqlRequest.put(JSONRequest.KEY_CACHE, parser.getGlobleCache());
      }
   }
}

上面五个if判断都是在parser获取数据库相关参数不为空,但是通过sqlrequest时发现对用的值为空,所以讲parser获取的值填入到sqlRequest中。

这五个参数在JSONObject中的注释分别为——

KEY_DATABASE:数据库类型,默认为MySQL

KEY_SCHEMA:数据库,Table在非默认schema内时需要声明

KEY_DATASOURCE:数据源

KEY_EXPLAIN:分析 true/false

KEY_CACHE:缓存 RAM/ROM/ALL

parser函数的最后一点点

if (isTable) { 
      onFunctionResponse("-");
   }

}

if (isInvalidate()) {
   recycle();
   return null;
}

return this;

非Table内的函数会被滞后在onChildParse后调用。

这里的onFunctionResponse函数在下面贴一下——

@Override
public void onFunctionResponse(String type) throws Exception {
   Map<String, String> map = functionMap == null ? null : functionMap.get(type);

   //解析函数function
   Set<Entry<String, String>> functionSet = map == null ? null : map.entrySet();
   if (functionSet != null && functionSet.isEmpty() == false) {
      JSONObject json = "-".equals(type) ? request : response; // key-():function 是实时执行,而不是在这里批量执行

      for (Entry<String, String> entry : functionSet) {
         parseFunction(entry.getKey(), entry.getValue(), parentPath, name, json);
      }
   }
}

在我看来是经过两次转换后把functionMap中的值转入functionset中,之后就根据值是否为空,以及传入的type来决定是实时执行还是批量执行。

最后就是执行parseFunction函数了。

最后仍会进行一次是否无效的判断,如果是Invalidate,就会对内存进行回收。以下是recycle()的函数——

@Override
public void recycle() {
   //后面还可能用到,要还原
   if (tri) {//避免返回未传的字段
      request.put(KEY_TRY, tri);
   }
   if (drop) {
      request.put(KEY_DROP, drop);
   }


   method = null;
   parentPath = null;
   arrayConfig = null;

   //    if (response != null) {
   //       response.clear();//有效果?
   //       response = null;
   //    }

   request = null;
   response = null;
   sqlRequest = null;
   sqlReponse = null;

   functionMap = null;
   customMap = null;
   childMap = null;
}

最后返回的则是该AbstractObjectParser类。

onParse

该函数主要用来解析普通成员。它在Parse函数执行的过程中其实有被使用到——

就是在本文一开始写的如果上述分类都不满足就会进入onParse中。

public boolean onParse(@NotNull String key, @NotNull Object value) throws Exception

传进来的参数是一对不为空的键值对。返回值也是bool类型的,在parse函数中,如果遇到执行onParse后返回的值是false,就会直接判定为无效化。

该函数下主要将key的值分为两大类一类是以@结尾,另一类则是以()结尾。

if (key.endsWith("@"))

if (value instanceof JSONObject)

if (key.endsWith("@")) {  // StringUtil.isPath((String) value)) {
   // [] 内主表 position > 0 时,用来生成 SQLConfig 的键值对全都忽略,不解析
   if (value instanceof JSONObject) {  // key{}@ getRealKey, SQL 子查询对象,JSONObject -> SQLConfig.getSQL
      String replaceKey = key.substring(0, key.length() - 1);

这里首先讨论以@结尾的情况。如果value是JSONObject类的话,就首先把最后一个字符去掉——即@。

substring 方法将返回一个包含从 start 到最后(不包含 end )的子字符串的字符串。

JSONObject subquery = (JSONObject) value;
String range = subquery.getString(JSONRequest.KEY_SUBQUERY_RANGE);
if (range != null && JSONRequest.SUBQUERY_RANGE_ALL.equals(range) == false && JSONRequest.SUBQUERY_RANGE_ANY.equals(range) == false) {
   throw new IllegalArgumentException("子查询 " + path + "/" + key + ":{ range:value } 中 value 只能为 [" + JSONRequest.SUBQUERY_RANGE_ALL + ", " + JSONRequest.SUBQUERY_RANGE_ANY + "] 中的一个!");
}

这里首先把value值转换为JSONObject类,然后再把值赋给了range。

下面就是针对range的值进行一个匹配,要求range的值如果不为空,就需要与SUBQUERY_RANGE_ALL或SUBQUERY_RANGE_ANY的值相同——即range只能为null或ALL或ANY

否则就会抛出IllegalArgumentException异常(错误参数异常)

JSONArray arr = parser.onArrayParse(subquery, path, key, true);

JSONObject obj = arr == null || arr.isEmpty() ? null : arr.getJSONObject(0);
if (obj == null) {
   throw new Exception("服务器内部错误,解析子查询 " + path + "/" + key + ":{ } 为 Subquery 对象失败!");
}

这里主要是对parser.onArrayParse函数的返回值进行判断,如果为null,也会抛出错误。

下面的也都类似

String from = subquery.getString(JSONRequest.KEY_SUBQUERY_FROM);
JSONObject arrObj = from == null ? null : obj.getJSONObject(from);
if (arrObj == null) {
   throw new IllegalArgumentException("子查询 " + path + "/" + key + ":{ from:value } 中 value 对应的主表对象 " + from + ":{} 不存在!");
}
//          
SQLConfig cfg = (SQLConfig) arrObj.get(AbstractParser.KEY_CONFIG);
if (cfg == null) {
   throw new NotExistException(TAG + ".onParse  cfg == null");
}

如果 throw 语句抛出的异常是 Runtime 异常,则该语句无须放在 try 块里,也无须放在带 throws 声明抛出的方法中;程序既可以显式使用 try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法调用者处理。

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

如果经过上述判断后,没有异常被抛出,就会进入最后打的赋值阶段

Subquery s = new Subquery();
s.setPath(path);
s.setOriginKey(key);
s.setOriginValue(subquery);

s.setFrom(from);
s.setRange(range);
s.setKey(replaceKey);
s.setConfig(cfg);

key = replaceKey;
value = s; //(range == null || range.isEmpty() ? "" : "range") + "(" + cfg.getSQL(false) + ") ";

parser.putQueryResult(AbstractParser.getAbsPath(path, key), s);//字符串引用保证不了安全性 parser.getSQL(cfg));

猜你喜欢

转载自blog.csdn.net/qq_50861917/article/details/121197321