首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Gson 深度實戰:Map<自定義對象, 值>、巢狀泛型集合、TypeToken 完整序列化與反序列化指南

     編輯:Java 相關知識

前言:Gson 處理複雜集合結構的核心挑戰

Gson 處理 List<User>、Map<String, User> 等標準結構已非常成熟,但當 Key 變成自定義對象 (Map<Point, String>)、Value 為泛型集合 (Map<String, List<Student>>) 時,類型擦除導致運行時無法獲取完整泛型資訊,必須借助 TypeToken 手動指定類型。本文將系統講解三大進階場景的完整序列化/反序列化實戰,並揭示 enableComplexMapKeySerialization() 等關鍵配置。


場景一:自定義對象作為 Map Key (Point → String)

實體類

public class Point { private int x, y; public Point(int x, int y) { this.x = x; this.y = y; } // getter/setter/toString 省略 }

關鍵配置:enableComplexMapKeySerialization()

預設 Gson 將 Map Key 調用 toString() 序列化為 JSON 字符串 Key (如 "Point@123"),反序列化時無法還原對象。開啟此選項後,Key 將序列化為 JSON 陣列 [[{keyObj}, value], ...],完整保留對象結構。

完整代碼

import com.google.gson.*; import java.util.*; public class GsonMapKeyDemo { public static void main(String[] args) { // 必須開啟複雜 Key 序列化 Gson gson = new GsonBuilder().enableComplexMapKeySerialization().create(); Map<Point, String> map = new LinkedHashMap<>(); map.put(new Point(5, 6), "A"); map.put(new Point(8, 8), "B"); // 序列化 String json = gson.toJson(map); System.out.println("JSON: " + json); // [[{"x":5,"y":6},"A"],[{"x":8,"y":8},"B"]] // 反序列化:TypeToken 捕獲完整泛型類型 Map<Point, String> restored = gson.fromJson(json, new TypeToken<Map<Point, String>>() {}.getType()); for (Point p : restored.keySet()) { System.out.println("Key: " + p + " → Value: " + restored.get(p)); } } }

輸出結果

JSON: [[{"x":5,"y":6},"A"],[{"x":8,"y":8},"B"]] Key: Point[x=5,y=6] → Value: A Key: Point[x=8,y=8] → Value: B

場景二:Map Value 為泛型集合 (String → List<Student>)

實體類

public class Student { private int id; private String name; private Date birthDay; // getter/setter/toString } public class Teacher { private int id; private String name; private String title; // getter/setter/toString }

完整代碼

public class GsonNestedGenericDemo { public static void main(String[] args) { Gson gson = new Gson(); // 構建測試資料 List<Student> students = Arrays.asList( new Student(1, "李坤", new Date()), new Student(2, "曹貴生", new Date()) ); List<Teacher> teachers = Arrays.asList( new Teacher(1, "米先生", "教授"), new Teacher(2, "丁先生", "講師") ); Map<String, Object> map = new LinkedHashMap<>(); map.put("students", students); map.put("teachers", teachers); // 序列化 String json = gson.toJson(map); System.out.println("JSON: " + json); // 反序列化:TypeToken 指定 Map> Map<String, Object> restored = gson.fromJson(json, new TypeToken<Map<String, List<Object>>>() {}.getType()); for (String key : restored.keySet()) { System.out.println("Key: " + key); List<Object> list = (List<Object>) restored.get(key); for (Object obj : list) { System.out.println("  " + obj); } } } }

輸出結果

JSON: {"students":[{"id":1,"name":"李坤","birthDay":"Jun 22, 2024..."},{"id":2,"name":"曹貴生","birthDay":"Jun 22, 2024..."}],"teachers":[{"id":1,"name":"米先生","title":"教授"},{"id":1,"name":"丁先生","title":"講師"}]} Key: students [Student[id=1,name=李坤,...], Student[id=2,name=曹貴生,...]] Key: teachers [Teacher[id=1,name=米先生,...], Teacher[id=2,name=丁先生,...]]

場景三:Map Key/Value 均為自定義對象 + 日期格式化

補充:日期格式化配置

Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd HH:mm:ss") // 統一日期格式 .enableComplexMapKeySerialization() .create();

完整代碼

public class GsonAdvancedMapDemo { public static void main(String[] args) { Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd HH:mm:ss") .enableComplexMapKeySerialization() .create(); Map<Point, List<Student>> complexMap = new LinkedHashMap<>(); complexMap.put(new Point(1, 1), Arrays.asList(new Student(1, "A", new Date()))); complexMap.put(new Point(2, 2), Arrays.asList(new Student(2, "B", new Date()))); String json = gson.toJson(complexMap); System.out.println(json); // 反序列化 Map<Point, List<Student>> restored = gson.fromJson(json, new TypeToken<Map<Point, List<Student>>>() {}.getType()); System.out.println("恢復成功: " + restored.size() + " 組"); } }

核心知識點總結表

場景關鍵配置TypeToken 寫法注意事項
Map<自定義Key, 值>enableComplexMapKeySerialization()new TypeToken<Map<Key, Value>>(){}.getType()Key 類需正確實作 equals/hashCode
Map<String, List<泛型>>無特殊配置new TypeToken<Map<String, List<T>>>(){}.getType()反序列化後 Value 為 List,需轉型
Map<Key, List<T>>enableComplexMapKeySerializationnew TypeToken<Map<Key, List<T>>>(){}.getType()雙重泛型嵌套,TypeToken 必須完整捕獲
日期格式統一setDateFormat("yyyy-MM-dd HH:mm:ss")無避免預設 Date 格式跨平台不一致

最佳實踐與避坑指南

  1. 統一使用 LinkedHashMap:保持插入順序,JSON 輸出穩定,便於測試對比與日誌閱讀。
  2. TypeToken 匿名內部類寫法:new TypeToken<...>(){}.getType() 利用匿名類保留泛型類型資訊,切勿寫成 TypeToken.getParameterized(Map.class, Key.class, Value.class) (後者無法處理巢狀泛型)。
  3. Key 類必須正確實作 equals/hashCode:否則反序列化後 Map 查找失效 (不同實例但邏輯相等)。
  4. 避免 Map<String, Object> 當萬用容器:反序列化失去類型資訊,改用明確泛型或自定義封裝類。
  5. 大數據量場景改用 Streaming API:JsonReader/JsonWriter 避免全量載入記憶體。
  6. 版本升級注意:Gson 2.10+ 預設關閉 enableComplexMapKeySerialization,舊代碼升級必須顯式開啟。

總結

Gson 處理複雜 Map 結構的核心在於:
① enableComplexMapKeySerialization() 讓自定義 Key 完整序列化
② TypeToken 匿名內部類捕獲完整泛型類型資訊
③ LinkedHashMap 保證順序穩定
掌握這三點,Map<Point, List<Student>> 這樣任意深度嵌套結構也能輕鬆序列化/反序列化。建議專案封裝 GsonUtils 統一配置,業務代碼一行調用,避免重複造輪子。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved