From c01a4e51c083b4ef9e167c6ec82d26609483444b Mon Sep 17 00:00:00 2001
From: 014-code <2402143478@qq.com>
Date: Sun, 26 Jul 2026 12:07:13 +0800
Subject: [PATCH] fix: make mixed CRUD method directives order-independent #809
---
APIJSONORM/pom.xml | 6 +
.../main/java/apijson/orm/AbstractParser.java | 191 +++++++++--------
.../AbstractParserMethodDirectiveTest.java | 193 ++++++++++++++++++
README-extend.md | 23 +++
4 files changed, 323 insertions(+), 90 deletions(-)
create mode 100644 APIJSONORM/src/test/java/apijson/orm/AbstractParserMethodDirectiveTest.java
diff --git a/APIJSONORM/pom.xml b/APIJSONORM/pom.xml
index 7a35eb015..692080bfb 100644
--- a/APIJSONORM/pom.xml
+++ b/APIJSONORM/pom.xml
@@ -21,6 +21,12 @@
+
+ junit
+ junit
+ 4.13.2
+ test
+
diff --git a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
index defccc938..26e63a9c9 100755
--- a/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
+++ b/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java
@@ -2303,6 +2303,86 @@ protected M getRequestStructure(RequestMethod method, String tag, int version) t
KEY_METHOD_ENUM_MAP.put(KEY_DELETE, RequestMethod.DELETE);
}
+ private void parseMethodDirective(String key, RequestMethod keyMethod, @NotNull M request) throws Exception {
+ boolean isPost = KEY_POST.equals(key);
+ Object val = request.get(key);
+ Map obj = val instanceof Map, ?> ? JSON.get(request, key) : null;
+ if (obj == null) {
+ if (val instanceof String) {
+ String[] tbls = StringUtil.split((String) val);
+ if (tbls != null && tbls.length > 0) {
+ obj = new LinkedHashMap();
+ for (String tbl : tbls) {
+ if (obj.containsKey(tbl)) {
+ throw new ConflictException(key + ": value 中 " + tbl + " 已经存在,不能重复!");
+ }
+
+ obj.put(tbl, isPost && isTableArray(tbl)
+ ? tbl.substring(0, tbl.length() - 2) + ":[]" : "");
+ }
+ }
+ }
+ else {
+ throw new IllegalArgumentException(key + ": value 中 value 类型错误,只能是 String 或 Map {} !");
+ }
+ }
+
+ Set> set = obj == null ? new HashSet<>() : obj.entrySet();
+ for (Entry objEntry : set) {
+ String objKey = objEntry == null ? null : objEntry.getKey();
+ if (objKey == null) {
+ continue;
+ }
+
+ Map objAttrMap = new HashMap<>();
+ objAttrMap.put(KEY_METHOD, keyMethod);
+ keyObjectAttributesMap.put(objKey, objAttrMap);
+
+ Object objVal = objEntry.getValue();
+ Map objAttrJson = objVal instanceof Map, ?> ? JSON.getMap(obj, objKey) : null;
+ if (objAttrJson == null) {
+ if (objVal instanceof String) {
+ objAttrMap.put(KEY_TAG, "".equals(objVal) ? objKey : objVal);
+ }
+ else {
+ throw new IllegalArgumentException(key + ": { " + objKey + ": value 中 value 类型错误,只能是 String 或 Map {} !");
+ }
+ }
+ else {
+ boolean hasTag = false;
+ for (Entry entry : objAttrJson.entrySet()) {
+ String objAttrKey = entry == null ? null : entry.getKey();
+ if (objAttrKey == null) {
+ continue;
+ }
+
+ switch (objAttrKey) {
+ case KEY_DATABASE:
+ case KEY_DATASOURCE:
+ case KEY_NAMESPACE:
+ case KEY_CATALOG:
+ case KEY_SCHEMA:
+ case KEY_VERSION:
+ case KEY_ROLE:
+ objAttrMap.put(objAttrKey, entry.getValue());
+ break;
+ case KEY_TAG:
+ hasTag = true;
+ objAttrMap.put(objAttrKey, entry.getValue());
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (hasTag == false) {
+ objAttrMap.put(KEY_TAG, isPost && isTableArray(objKey)
+ ? objKey.substring(0, objKey.length() - 2) + ":[]" : objKey);
+ }
+ }
+ }
+ }
+
protected M batchVerify(RequestMethod method, String tag, int version, String name, @NotNull M request, int maxUpdateCount, SQLCreator creator) throws Exception {
M correctRequest = JSON.createJSONObject();
List removeTmpKeys = new ArrayList<>(); // 请求json里面的临时变量,不需要带入后面的业务中,比如 @post、@get等
@@ -2312,103 +2392,34 @@ protected M batchVerify(RequestMethod method, String tag, int version, String na
throw new IllegalArgumentException("JSON 对象格式不正确 !正确示例例如 \"User\": {}");
}
+ // 先收集所有显式方法,避免同一请求中方法指令的字段顺序影响对象解析结果。
for (String key : reqSet) {
- // key 重复直接抛错(xxx:alias, xxx:alias[])
- if (correctRequest.containsKey(key) || correctRequest.containsKey(key + KEY_ARRAY)) {
- throw new IllegalArgumentException("对象名重复,请添加别名区分 ! 重复对象名为: " + key);
+ RequestMethod keyMethod = KEY_POST.equals(key) ? RequestMethod.POST : KEY_METHOD_ENUM_MAP.get(key);
+ if (keyMethod == null) {
+ continue;
}
- boolean isPost = KEY_POST.equals(key);
- // @post、@get 等 RequestMethod
+ removeTmpKeys.add(key);
try {
- RequestMethod keyMethod = isPost ? RequestMethod.POST : KEY_METHOD_ENUM_MAP.get(key);
- if (keyMethod != null) {
- // 如果不匹配,异常不处理即可
- removeTmpKeys.add(key);
-
- Object val = request.get(key);
- Map obj = val instanceof Map, ?> ? JSON.get(request, key) : null;
- if (obj == null) {
- if (val instanceof String) {
- String[] tbls = StringUtil.split((String) val);
- if (tbls != null && tbls.length > 0) {
- obj = new LinkedHashMap();
- for (int i = 0; i < tbls.length; i++) {
- String tbl = tbls[i];
- if (obj.containsKey(tbl)) {
- throw new ConflictException(key + ": value 中 " + tbl + " 已经存在,不能重复!");
- }
-
- obj.put(tbl, isPost && isTableArray(tbl)
- ? tbl.substring(0, tbl.length() - 2) + ":[]" : "");
- }
- }
- }
- else {
- throw new IllegalArgumentException(key + ": value 中 value 类型错误,只能是 String 或 Map {} !");
- }
- }
-
- Set> set = obj == null ? new HashSet<>() : obj.entrySet();
-
- for (Entry objEntry : set) {
- String objKey = objEntry == null ? null : objEntry.getKey();
- if (objKey == null) {
- continue;
- }
-
- Map objAttrMap = new HashMap<>();
- objAttrMap.put(KEY_METHOD, keyMethod);
- keyObjectAttributesMap.put(objKey, objAttrMap);
-
- Object objVal = objEntry.getValue();
- Map objAttrJson = objVal instanceof Map, ?> ? JSON.getMap(obj, objKey) : null;
- if (objAttrJson == null) {
- if (objVal instanceof String) {
- objAttrMap.put(KEY_TAG, "".equals(objVal) ? objKey : objVal);
- }
- else {
- throw new IllegalArgumentException(key + ": { " + objKey + ": value 中 value 类型错误,只能是 String 或 Map {} !");
- }
- }
- else {
- Set> objSet = objAttrJson.entrySet();
-
- boolean hasTag = false;
- for (Entry entry : objSet) {
- String objAttrKey = entry == null ? null : entry.getKey();
- if (objAttrKey == null) {
- continue;
- }
+ parseMethodDirective(key, keyMethod, request);
+ }
+ catch (Exception e) {
+ Log.e(TAG, "parse method directive failed", e);
+ throw e;
+ }
+ }
- switch (objAttrKey) {
- case KEY_DATABASE:
- case KEY_DATASOURCE:
- case KEY_NAMESPACE:
- case KEY_CATALOG:
- case KEY_SCHEMA:
- case KEY_VERSION:
- case KEY_ROLE:
- objAttrMap.put(objAttrKey, entry.getValue());
- break;
- case KEY_TAG:
- hasTag = true;
- objAttrMap.put(objAttrKey, entry.getValue());
- break;
- default:
- break;
- }
- }
+ for (String key : reqSet) {
+ // key 重复直接抛错(xxx:alias, xxx:alias[])
+ if (correctRequest.containsKey(key) || correctRequest.containsKey(key + KEY_ARRAY)) {
+ throw new IllegalArgumentException("对象名重复,请添加别名区分 ! 重复对象名为: " + key);
+ }
- if (hasTag == false) {
- objAttrMap.put(KEY_TAG, isPost && isTableArray(objKey)
- ? objKey.substring(0, objKey.length() - 2) + ":[]" : objKey);
- }
- }
- }
- continue;
- }
+ if (KEY_POST.equals(key) || KEY_METHOD_ENUM_MAP.containsKey(key)) {
+ continue;
+ }
+ try {
// 1、非crud,对于没有显式声明操作方法的,直接用 URL(/get, /post 等) 对应的默认操作方法
// 2、crud, 没有声明就用 GET
// 3、兼容 sql@ Map,设置 GET方法
diff --git a/APIJSONORM/src/test/java/apijson/orm/AbstractParserMethodDirectiveTest.java b/APIJSONORM/src/test/java/apijson/orm/AbstractParserMethodDirectiveTest.java
new file mode 100644
index 000000000..dc96bfb0b
--- /dev/null
+++ b/APIJSONORM/src/test/java/apijson/orm/AbstractParserMethodDirectiveTest.java
@@ -0,0 +1,193 @@
+package apijson.orm;
+
+import apijson.JSON;
+import apijson.JSONParser;
+import apijson.RequestMethod;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AbstractParserMethodDirectiveTest {
+ private static JSONParser extends Map, ? extends List