{"record":{"id":"356820b74011bc05","repo":"Tencent/APIJSON","slug":"join-value-value-table-table0","errorCode":null,"errorMessage":"@join:value 中 value 的 Table 值 {} 不合法！必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式！且 Table 必须满足大写字母开头的表对象英文单词 key 格式！","messagePattern":"@join:value 中 value 的 Table 值 (.+?) 不合法！必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,\\.\\.\\. 或 (.+?), '</Table1/key1':(.+?),\\.\\.\\. \\} 这种格式！且 Table 必须满足大写字母开头的表对象英文单词 key 格式！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1639,"sourceCode":"\t\t\t//\t\t\t\tjoinType = \"|\"; // FULL JOIN\n\t\t\t//\t\t\t}\n\t\t\tpath = path.substring(index + 1);\n\n\t\t\tindex = path.lastIndexOf(\"/\");\n\t\t\tString tableKey = index < 0 ? path : path.substring(0, index); // User:owner\n\t\t\tint index2 = tableKey.lastIndexOf(\"/\");\n\t\t\tString arrKey = index2 < 0 ? null : tableKey.substring(0, index2);\n\t\t\tif (arrKey != null && isArrayKey(arrKey) == false) {\n\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":'\" + e.getKey() + \"' 对应的 \" + arrKey + \" 不是合法的数组 key[] ！\" +\n\t\t\t\t\t\t\"@ APP JOIN 最多允许跨 1 层，只能是子数组，且数组对象中不能有 join: value 键值对！\");\n\t\t\t}\n\n\t\t\ttableKey = index2 < 0 ? tableKey : tableKey.substring(index2+1);\n\n\t\t\tapijson.orm.Entry<String, String> entry = Pair.parseEntry(tableKey, true);\n\t\t\tString table = entry.getKey(); // User\n\t\t\tif (StringUtil.isName(table) == false) {\n\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":value 中 value 的 Table 值 \" + table + \" 不合法！\"\n\t\t\t\t\t\t+ \"必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式！\"\n\t\t\t\t\t\t+ \"且 Table 必须满足大写字母开头的表对象英文单词 key 格式！\");\n\t\t\t}\n\n\t\t\tString alias = entry.getValue(); // owner\n\t\t\tif (StringUtil.isNotEmpty(alias, true) && StringUtil.isName(alias) == false) {\n\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":value 中 value 的 alias 值 \" + alias + \" 不合法！\"\n\t\t\t\t\t\t+ \"必须为 &/Table0,</Table1/key1,@/Table1:alias2/key2,... 或 { '&/Table0':{}, '</Table1/key1':{},... } 这种格式！\"\n\t\t\t\t\t\t+ \"且 Table:alias 的 alias 必须满足英文单词变量名格式！\");\n\t\t\t}\n\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) {","sourceCodeStart":1621,"sourceCodeEnd":1657,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1621-L1657","documentation":"After splitting the join path, the table segment is parsed (Table:alias) and validated with StringUtil.isName — must be a valid identifier word (and by APIJSON convention an uppercase-initial table object name). Failure throws IllegalArgumentException showing the offending table and the full expected grammar.","triggerScenarios":"\"@join\":\"&/user/id\" (lowercase start), table with digits at start, dashes, spaces, or SQL characters: '&/User-2/id', '@/u ser/x'. Any table token failing isName, including attempts to inject SQL through the table slot.","commonSituations":"Using actual DB table names that are lowercase or contain underscores where the ORM expects the camel/uppercase object key; SQL injection attempts via @join (this check is the guard); renaming tables without updating joins.","solutions":["Use the APIJSON table-object key spelling (uppercase first letter, word characters only), e.g. '&/User/id'","If your table name is unconventional, map it via the table rename/alias config rather than the raw name","Never build @join from unescaped user input; whitelist table names server-side","Keep alias in the Table:alias slot, not glued into the table token"],"exampleFix":"// before\n{\"@join\":\"&/sys_user/id\"}\n// after\n{\"@join\":\"&/SysUser/id\"}","handlingStrategy":"validation","validationCode":"if (!table.matches(\"^[A-Z][A-Za-z0-9_]*$\")) throw new IllegalArgumentException(\"bad join table: \" + table);","typeGuard":"boolean isValidJoinTable(String t) { return t != null && t.matches(\"^[A-Z][A-Za-z0-9_]*$\"); }","tryCatchPattern":null,"preventionTips":["Whitelist table names; never interpolate user input into @join","Map unconventional DB names to proper APIJSON object keys","Assert isName-compatible casing in payload tests"],"tags":["apijson","join","table-name","identifier-validation","sql-injection-guard"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}