{"record":{"id":"a196771860587a6a","repo":"Tencent/APIJSON","slug":"join-e-getkey-arrkey-join-value","errorCode":null,"errorMessage":"join:'${e.getKey()}' 对应的 ${arrKey}:{ join: value } 中 value 不合法！@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中不能有 join: value 键值对！","messagePattern":"join:'(.+?)' 对应的 (.+?):(.+?) 中 value 不合法！@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中不能有 join: value 键值对！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1668,"sourceCode":"\n\t\t\t// 取出Table对应的JSONObject，及内部引用赋值 key:value\n\t\t\tM tableObj;\n\t\t\tM parentPathObj;\t// 保留\n\t\t\ttry {\n\t\t\t\tparentPathObj = arrKey == null ? request : JSON.get(request, arrKey);\t// 保留\n\t\t\t\ttableObj = parentPathObj == null ? null : JSON.get(parentPathObj, tableKey);\n\t\t\t\tif (tableObj == null) {\n\t\t\t\t\tthrow new NullPointerException(\"tableObj == null\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch (Exception e2) {\n\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + tableKey + \":value 中 value 类型不合法！\" +\n          \t\t\t\"必须是 {} 这种 Map<String, Object> 格式！\" + e2.getMessage());\n\t\t\t}\n\n\t\t\tif (arrKey != null) {\n\t\t\t\tif (parentPathObj.get(apijson.JSONRequest.KEY_JOIN) != null) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + arrKey + \":{ join: value } 中 value 不合法！\" +\n\t\t\t\t\t\t\t\"@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中不能有 join: value 键值对！\");\n\t\t\t\t}\n\n\t\t\t\tInteger subPage = getInteger(parentPathObj, apijson.JSONRequest.KEY_PAGE);\n\t\t\t\tif (subPage != null && subPage != 0) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + arrKey + \":{ page: value } 中 value 不合法！\" +\n\t\t\t\t\t\t\t\"@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中 page 值只能为 null 或 0 ！\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tboolean isAppJoin = \"@\".equals(joinType);\n\n\t\t\tM refObj = JSON.createJSONObject();\n\n\t\t\tString key = index < 0 ? null : path.substring(index + 1); // id@\n\t\t\tif (key != null) {  // 指定某个 key 为 JOIN ON 条件\n\t\t\t\tif (key.indexOf(\"@\") != key.length() - 1) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":\" + e.getKey() + \" 中 \" + key + \" 不合法！\"","sourceCodeStart":1650,"sourceCodeEnd":1686,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1650-L1686","documentation":"For @ APP JOIN targeting a sub-array (arrKey present), the sub-array's object must NOT itself contain a join key and its page must be null/0. If parentPathObj.get(\"join\") is non-null, IllegalArgumentException is thrown: APP JOIN may span only one level and cannot nest further join definitions.","triggerScenarios":"{\"@join\":\"@/list[]/User\", \"list[]\":{\"join\":\"&/X\", ...}} — an inner join inside the array object being app-joined. The sibling page check (next lines) similarly rejects page!=0 because app-join fetches must be single-page.","commonSituations":"Composing query builders that merge join clauses into every level; converting an SQL multi-join request to APP JOIN by only flipping sigils; UI presets injecting default page values into every nested array.","solutions":["Remove the inner join key from the sub-array object; move needed joins to the top level or use SQL join types","Set the sub-array's page to 0 or remove it entirely","Restructure: perform the nested join as a separate request and stitch results by reference","Reserve @ APP JOIN for the simple one-level fan-out case"],"exampleFix":"// before\n{\"@join\":\"@/list[]/User\",\"list[]\":{\"join\":\"&/Tag\",\"page\":1,\"User\":{}}}\n// after\n{\"@join\":\"@/list[]/User\",\"list[]\":{\"User\":{}}}","handlingStrategy":"validation","validationCode":"Map<?,?> arrObj = (Map<?,?>) root.get(arrKey);\nif (arrObj != null && arrObj.get(\"join\") != null) throw new IllegalStateException(\"app-join sub-array must not contain join\");\nif (arrObj != null && Integer.valueOf(0).equals(arrObj.get(\"page\")) == false && arrObj.get(\"page\") != null) throw new IllegalStateException(\"page must be 0\");","typeGuard":"boolean subArrayCleanForAppJoin(Map<?,?> arrObj) {\n  return arrObj != null && arrObj.get(\"join\") == null && (arrObj.get(\"page\") == null || Integer.valueOf(0).equals(arrObj.get(\"page\")));}\n","tryCatchPattern":null,"preventionTips":["Keep app-joined sub-arrays minimal: only the table object inside","Move additional joins to the top level or use SQL join types","Strip default page values that query builders inject into every array"],"tags":["apijson","app-join","nested-join","request-format"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}