{"record":{"id":"03e125c54ffacc82","repo":"Tencent/APIJSON","slug":"abstractparser-onjoinparse-join-string-map","errorCode":null,"errorMessage":"AbstractParser.onJoinParse  join 只能是 String 或 Map<String, Object> 类型！","messagePattern":"AbstractParser\\.onJoinParse  join 只能是 String 或 Map<String, Object> 类型！","errorType":"validation","errorClass":"UnsupportedDataTypeException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1561,"sourceCode":"\t * @throws Exception\n\t */\n\tprivate List<Join<T, M, L>> onJoinParse(Object join, M request) throws Exception {\n\t\tMap<String, Object> joinMap = null;\n\n\t\tif (join instanceof Map<?, ?>) {\n\t\t\tjoinMap = (M) join;\n\t\t}\n\t\telse if (join instanceof String) {\n\t\t\tString[] sArr = request == null || request.isEmpty() ? null : StringUtil.split((String) join);\n\t\t\tif (sArr != null && sArr.length > 0) {\n\t\t\t\tjoinMap = new LinkedHashMap<String, Object>(); //注意：这里必须要保证join连接顺序，保证后边遍历是按照join参数的顺序生成的SQL\n\t\t\t\tfor (int i = 0; i < sArr.length; i++) {\n\t\t\t\t\tjoinMap.put(sArr[i], new LinkedHashMap<String, Object>());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse if (join != null){\n\t\t\tthrow new UnsupportedDataTypeException(TAG + \".onJoinParse  join 只能是 String 或 Map<String, Object> 类型！\");\n\t\t}\n\n\t\tList<Entry<String, Object>> slashKeys = new ArrayList<>();\n\t\tList<Entry<String, Object>> nonSlashKeys = new ArrayList<>();\n\t\tSet<Entry<String, Object>> entries = joinMap == null ? null : joinMap.entrySet();\n\n\t\tif (entries == null || entries.isEmpty()) {\n\t\t\tLog.e(TAG, \"onJoinParse  set == null || set.isEmpty() >> return null;\");\n\t\t\treturn null;\n\t\t}\n\t\tfor (Entry<String, Object> e : entries) {\n\t\t\tString path = e.getKey();\n\t\t\tif (path != null && path.indexOf(\"/\") > 0) {\n\t\t\t\tslashKeys.add(e);  // 以 / 开头的 key，例如 </Table/key@\n\t\t\t} else {\n\t\t\t\tnonSlashKeys.add(e);  // 普通 key，例如 Table: {}\n\t\t\t}\n\t\t}","sourceCodeStart":1543,"sourceCodeEnd":1579,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1543-L1579","documentation":"The @join value may be a String (\"&/Table0/key0,</Table1/key1\") or a Map of such entries. onJoinParse type-checks it; any other JSON type (number, boolean, array) throws UnsupportedDataTypeException naming the two allowed types. null is tolerated (skipped).","triggerScenarios":"\"@join\": [\"&/User/id\"] (array of strings), \"@join\": 123, \"@join\": true. Note a JSON array is NOT accepted even though it looks natural — only String or Object.","commonSituations":"Client builds join list as an array because that is idiomatic elsewhere; templating engines render a list where a string was expected; copy from docs that show the map form but serialize wrongly.","solutions":["Send @join as a single string: \"&/User/id,</Moment/userId\"","Or as an object whose values are objects: {\"&/User/id\":{}}","Never use a JSON array for @join","Validate the wire format in tests with a schema assertion"],"exampleFix":"// before\n{\"@join\":[\"&/User/id\"]}\n// after\n{\"@join\":\"&/User/id\"}","handlingStrategy":"type-guard","validationCode":"Object j = req.get(\"@join\");\nif (j != null && !(j instanceof String) && !(j instanceof Map)) throw new IllegalArgumentException(\"@join must be String or Map\");","typeGuard":"boolean joinValueIsLegalType(Object join) {\n  return join == null || join instanceof String || join instanceof Map;\n}","tryCatchPattern":null,"preventionTips":["Always emit @join as a single string","Reject array-typed @join in request builders","Add a schema test: @join type in [string, object]"],"tags":["apijson","join","type-validation","sql"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}