{"record":{"id":"4e99014257034a86","repo":"Tencent/APIJSON","slug":"key","errorCode":null,"errorMessage":"对象名重复,请添加别名区分 ! 重复对象名为: ${key}","messagePattern":"对象名重复,请添加别名区分 ! 重复对象名为: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":2415,"sourceCode":"\t\t\tRequestMethod keyMethod = KEY_POST.equals(key) ? RequestMethod.POST : KEY_METHOD_ENUM_MAP.get(key);\n\t\t\tif (keyMethod == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tremoveTmpKeys.add(key);\n\t\t\ttry {\n\t\t\t\tparseMethodDirective(key, keyMethod, request);\n\t\t\t}\n\t\t\tcatch (Exception e) {\n\t\t\t\tLog.e(TAG, \"parse method directive failed\", e);\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t}\n\n\t\tfor (String key : reqSet) {\n\t\t\t// key 重复直接抛错(xxx:alias, xxx:alias[])\n\t\t\tif (correctRequest.containsKey(key) || correctRequest.containsKey(key + KEY_ARRAY)) {\n\t\t\t\tthrow new IllegalArgumentException(\"对象名重复,请添加别名区分 ! 重复对象名为: \" + key);\n\t\t\t}\n\n\t\t\tif (KEY_POST.equals(key) || KEY_METHOD_ENUM_MAP.containsKey(key)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// 1、非crud,对于没有显式声明操作方法的，直接用 URL(/get, /post 等) 对应的默认操作方法\n\t\t\t\t// 2、crud, 没有声明就用 GET\n\t\t\t\t// 3、兼容 sql@ Map<String, Object>,设置 GET方法\n\t\t\t\t// 将method 设置到每个object, op执行会解析\n\t\t\t\tObject obj = request.get(key);\n\n\t\t\t\tif (obj instanceof Map<?, ?>) {\n\t\t\t\t\tMap<String, Object> attrMap = keyObjectAttributesMap.get(key);\n\n\t\t\t\t\tif (attrMap == null) {\n\t\t\t\t\t\t// 数组会解析为对象进行校验,做一下兼容","sourceCodeStart":2397,"sourceCodeEnd":2433,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L2397-L2433","documentation":"During batchVerify, a key in the request already exists in correctRequest either as-is or with the array suffix — i.e. the same table object name appears twice (including xxx vs xxx[]). APIJSON cannot map two identical object names to one result slot, so it demands an alias.","triggerScenarios":"A JSON body containing two \"User\" keys, or both \"User\" and \"User[]\"; duplicate keys survive when JSON is built from a map that silently keeps the last entry client-side but a raw string body keeps both.","commonSituations":"Client merges two request fragments that both query the same table; wanting the same table twice with different conditions (e.g. one row and a list) without knowing about the alias syntax Table:alias.","solutions":["Alias the second occurrence: \"User\": {...} and \"User:adminList[]\": {...}","Deduplicate keys when assembling the request object client-side","Use distinct aliases for every repeat of the same table in one request"],"exampleFix":"// before\n{\"User\": {\"id\": 1}, \"User\": {\"sex\": 1}}\n// after\n{\"User\": {\"id\": 1}, \"User:femaleList[]\": {\"sex\": 1}}","handlingStrategy":"validation","validationCode":"const seen = new Set();\nfor (const k of Object.keys(req)) {\n  const base = k.replace(/:.*$/, '');\n  if (seen.has(base)) throw new Error(`duplicate table object ${base}; use alias Table:xxx`);\n  seen.add(base);\n}","typeGuard":"const isUniqueTableKey = (keys, k) => !keys.some(x => x.replace(/:.*$/, '') === k.replace(/:.*$/, '') && x !== k);","tryCatchPattern":"try { await client.crud(req); } catch (e) { if (e.message.includes('对象名重复')) autoAliasAndRetry(req, e); else throw e; }","preventionTips":["Always alias same-table repeats up front (User:a, User:b)","Avoid building request JSON via string concatenation where dup keys survive"],"tags":["apijson","duplicate-key","alias"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}