前端傳遞陣列(特別是物件陣列)至後端是常見需求,但 $.ajax/$.post 預設序列化行為與後端接收機制存在差異。本文系統梳理傳輸格式、後端接收、JSON 反序列化全鏈路實現。
// 錯誤示範:直接傳遞物件陣列
var arr = [{id:1, name:'A'}, {id:2, name:'B'}];
$.post("/api/save", { data: arr });
// 後端接收到:data[0] = "[object Object]" (字串),非結構化資料
根因:jQuery 內部呼叫.toString()將物件轉為"[object Object]",後端request.getParameterValues()僅能接收String[]。
var jsonArray = [];
for (var i = 0; i < 5; i++) {
// 手動拼接 JSON 字串(或使用 JSON.stringify)
var item = '{"k":' + i + ',"v":' + i + '}';
jsonArray.push(item);
}
// 發送請求:參數名建議加 [] 後綴,符合後端慣例
$.post("servlet/AjaxServlet?m=putWarningRule", { "aa[]": jsonArray });
JSON.stringifyvar objects = [
{ id: 1, name: "Item 1", tags: ["tag1", "tag2"] },
{ id: 2, name: "Item 2", tags: ["tag3"] }
];
// 發送單一 JSON 字串(推薦)
$.ajax({
url: "/api/batchSave",
type: "POST",
contentType: "application/json", // 關鍵:告知後端請求體為 JSON
data: JSON.stringify({ items: objects }),
success: function(resp) { console.log(resp); }
});
application/x-www-form-urlencoded)@WebServlet("/servlet/AjaxServlet")
public class AjaxServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
// 關鍵:參數名需與前端一致,含 []
String[] jsonArr = req.getParameterValues("aa[]");
if (jsonArr != null) {
for (String json : jsonArr) {
Person p = JSONUtils.jsonToObject(json, Person.class);
// 業務處理...
}
}
}
}
@RestController
@RequestMapping("/api")
public class BatchController {
@PostMapping("/batchSave")
public ResponseEntity<String> batchSave(@RequestBody BatchRequest request) {
List<Person> items = request.getItems();
// 直接獲得 List,無需手動解析
return ResponseEntity.ok("Saved " + items.size() + " items");
}
// DTO 定義
public static class BatchRequest {
private List<Person> items;
// getter/setter
}
}
public class JSONUtils {
/** 物件轉 JSON 字串 */
public static String toJSONAsString(Object obj) {
try {
return JSONMapper.toJSON(obj).render(false);
} catch (MapperException e) {
e.printStackTrace();
}
return null;
}
/** JSON 字串轉物件(泛型) */
@SuppressWarnings("unchecked")
public static <T> T jsonToObject(String jsonStr, Class<T> targetClass)
throws TokenStreamException, RecognitionException, MapperException {
JSONValue jv = new JSONParser(new StringReader(jsonStr)).nextValue();
return (T) JSONMapper.toJava(jv, targetClass);
}
// 測試
public static void main(String[] args) throws Exception {
Person p = new Person();
p.setK("a"); p.setV("v");
String json = toJSONAsString(p);
Person np = jsonToObject(json, Person.class);
System.out.println(np.getK() + "==" + np.getV());
}
}
<dependency>
<groupId>com.metaparadigm</groupId>
<artifactId>jsontools-core</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
巢狀物件(如 Person.address)同樣適用,JSON 字串完整保留結構:
// 前端
var person = {
username: "john",
password: "123",
address: { city: "Taipei", zip: "100" }
};
$.ajax({
url: "/api/savePerson",
type: "POST",
contentType: "application/json",
data: JSON.stringify(person)
});
// 後端 DTO
public class Person {
private String username;
private String password;
private Address address; // 內嵌物件
// getters/setters
}
public class Address {
private String city;
private String zip;
// getters/setters
}
// Servlet 中列印所有參數名稱
Enumeration<String> names = request.getParameterNames();
while (names.hasMoreElements()) {
String name = names.nextElement();
System.out.println("Param: " + name + " = " + Arrays.toString(request.getParameterValues(name)));
}
// 輸出範例:Param: aa[] = ["{\"k\":0,\"v\":0}", "{\"k\":1,\"v\":1}", ...]
| 方案 | 前端 Content-Type | 後端接收方式 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|---|---|
| 表單陣列 | application/x-www-form-urlencoded | request.getParameterValues("aa[]") | 簡單陣列、舊專案 | 相容性好 | 需手動 JSON 序列化/反序列化 |
| JSON 請求體 | application/json | @RequestBody DTO / JSON.parse | 現代 REST API、複雜物件 | 型別安全、自動綁定、支援巢狀 | 需 Spring/Jackson 或手動解析 |
| FormData | multipart/form-data | request.getPart / @RequestPart | 檔案上傳混合資料 | 支援二進制檔案 | 處理較複雜 |
最佳實踐建議:
1. 新專案統一使用 JSON 請求體 + @RequestBody,前端JSON.stringify,後端 Jackson 自動綁定。
2. 避免手動拼接 JSON 字串,防止 XSS/注入與語法錯誤。
3. 統一日期格式(ISO 8601:yyyy-MM-dd'T'HH:mm:ss.SSSZ)。
4. 定義明確的 DTO 類別,拒絕Map<String, Object>模糊類型。
5. 開啟 JacksonFAIL_ON_UNKNOWN_PROPERTIES=false兼容版本演進。