{"record":{"id":"0acbb20e85fe6f3a","repo":"Tencent/APIJSON","slug":"key-tablekey-0acbb2","errorCode":null,"errorMessage":"{}/{}:{} 不合法！数组 []:{} 中每个 key:{} 都必须是表 TableKey:{} 或 数组 arrayKey[]:{} ！","messagePattern":"(.+?)/(.+?):(.+?) 不合法！数组 \\[\\]:(.+?) 中每个 key:(.+?) 都必须是表 TableKey:(.+?) 或 数组 arrayKey\\[\\]:(.+?) ！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java","lineNumber":641,"sourceCode":"\t\t\t\tString infoKey = JSONResponse.formatArrayKey(key) + \"Info\";\n\t\t\t\tif ((request.containsKey(totalKey) || request.containsKey(infoKey)\n\t\t\t\t\t\t|| request.containsKey(totalKey + \"@\") || request.containsKey(infoKey + \"@\")) == false) {\n\t\t\t\t\t// onParse(\"total@\", \"/\" + key + \"/total\");\n\t\t\t\t\t// onParse(infoKey + \"@\", \"/\" + key + \"/info\");\n\t\t\t\t\t// 替换为以下性能更好、对流程干扰最小的方式：\n\n\t\t\t\t\tString keyPath = AbstractParser.getValuePath(type == TYPE_ITEM ? path : parentPath, \"/\" + key);\n\t\t\t\t\tString totalPath = keyPath + \"/total\";\n\t\t\t\t\tString infoPath = keyPath + \"/info\";\n\t\t\t\t\tresponse.put(totalKey, onReferenceParse(totalPath));\n\t\t\t\t\tresponse.put(infoKey, onReferenceParse(infoPath));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse { //APIJSON Object\n\t\t\tboolean isTableKey = JSONMap.isTableKey(Pair.parseEntry(key, true).getKey());\n\t\t\tif (type == TYPE_ITEM && isTableKey == false) {\n\t\t\t\tthrow new IllegalArgumentException(parentPath + \"/\" + key + \":{} 不合法！\"\n\t\t\t\t\t\t+ \"数组 []:{} 中每个 key:{} 都必须是表 TableKey:{} 或 数组 arrayKey[]:{} ！\");\n\t\t\t}\n\n\t\t\tif ( //避免使用 \"test\":{\"Test\":{}} 绕过限制，实现查询爆炸   isTableKey &&\n\t\t\t\t\t(arrayConfig == null || arrayConfig.getPosition() == 0)) {\n\t\t\t\tobjectCount ++;\n\t\t\t\tint maxObjectCount = parser.getMaxObjectCount();\n\t\t\t\tif (objectCount > maxObjectCount) {  //TODO 这里判断是批量新增/修改，然后上限为 maxUpdateCount\n\t\t\t\t\tthrow new IllegalArgumentException(path + \" 内截至 \" + key + \":{} 时对象\"\n\t\t\t\t\t\t\t+ \" key:{} 的数量达到 \" + objectCount + \" 已超限，必须在 0-\" + maxObjectCount + \" 内 !\");\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchild = parser.onObjectParse(value, path, key, isMain ? arrayConfig.setType(SQLConfig.TYPE_ITEM_CHILD_0) : null\n\t\t\t\t\t, isSubquery, cache instanceof Map<?, ?> ? (M) cache : null);\n\n\t\t\tisEmpty = child == null || ((Map<?, ?>) child).isEmpty();\n\t\t\tif (isFirst && isEmpty) {","sourceCodeStart":623,"sourceCodeEnd":659,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractObjectParser.java#L623-L659","documentation":"Within an array item context (type == TYPE_ITEM), every non-array key must be a table key ('TableKey':{}). When a child key of an array item is neither an array key (handled above) nor a valid table key per JSONMap.isTableKey (after Pair.parseEntry strips alias), this IllegalArgumentException is thrown — non-table keys like 'total', 'info' or arbitrary names are not allowed directly inside '[]'.","triggerScenarios":"\"[]\": { \"User\": { ... }, \"total\": 3 } — placing a plain key such as 'total', 'tag' or 'My alias':{} (unparseable/invalid table key) directly inside the array item template.","commonSituations":"Adding pagination hints or metadata inside '[]' instead of beside it; using keys starting with lowercase or containing characters that fail isTableKey; forgetting that only table objects may sit at array-item level.","solutions":["Move non-table keys out of '[]' to the enclosing object: { \"[]\": { \"User\": {} }, \"total\": 3 }.","Ensure every key inside the array item is a valid table key (or a nested 'key[]' array).","Use 'query': 2 on the array to get total/info instead of adding them by hand."],"exampleFix":"// before\n{ \"[]\": { \"User\": { \"sex\": 1 }, \"total\": 3 } }\n// after\n{ \"[]\": { \"User\": { \"sex\": 1 } }, \"query\": 2 }","handlingStrategy":"validation","validationCode":"for (String k : arrayItemRequest.keySet()) {\n  if (!k.endsWith(\"[]\") && !JSONMap.isTableKey(Pair.parseEntry(k, true).getKey())) {\n    throw new IllegalArgumentException(k + \" inside [] must be a TableKey or arrayKey[]\");\n  }\n}","typeGuard":"const TABLE_KEY = /^[A-Za-z][A-Za-z0-9_]*$/; // adjust to your table naming rule\nfunction arrayChildKeysValid(keys: string[]): boolean {\n  return keys.every(k => k.endsWith('[]') || TABLE_KEY.test(k));\n}","tryCatchPattern":null,"preventionTips":["Only table objects and nested arrays go directly inside '[]'.","Put total/info and other metadata outside '[]'; use 'query': 2 for totals.","Validate array-item keys with the table-key rule before sending."],"tags":["apijson","array","table-key","validation","request-syntax"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}