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

Gson 完整實戰指南:Java 對象 JSON 序列化與反序列化 (Bean/Collection/Array/Map/巢狀/日期/TypeAdapter) 全場景範例

     編輯:Java 相關知識

前言:Gson 核心雙雄與類型擦除挑戰

Gson 以 toJson(Object) / fromJson(json, Type) 兩大核心 API 統天下。單對象、基本集合轉換極其簡單,但遇到 泛型集合 (List<T>)、Map<K, V>、陣列 T[]、巢狀泛型、日期、BigDecimal、枚舉、循環引用 時,必須理解 TypeToken 捕獲泛型類型資訊、GsonBuilder 註冊 TypeAdapter 客製化序列化邏輯。本文提供一份涵蓋所有主流場景的完整可運行範例庫,作為專案開發隨查隨用的速查手冊。


環境準備

<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency>

實體類定義 (多類型測試用)

public class User { private Long id; private String name; private int age; private LocalDateTime createTime; private BigDecimal balance; private Status status; // enum private Address address; // 巢狀對象 private List<String> tags; // getter/setter/toString 省略 public enum Status { ACTIVE, INACTIVE, BANNED } public static class Address { private String city; private String street; // getter/setter } }

場景一:單一 Bean (最基礎)

Gson gson = new Gson(); User user = new User(1L, "張三", 25, LocalDateTime.now(), new BigDecimal("12345.67"), User.Status.ACTIVE, new User.Address("台北", "信義路"), Arrays.asList("VIP", "早期用戶")); String json = gson.toJson(user); User restored = gson.fromJson(json, User.class);

場景二:List 集合 (關鍵:TypeToken)

List<User> list = Arrays.asList(user1, user2); String json = gson.toJson(list); // 反序列化:必須用 TypeToken 捕獲 List 完整類型 List<User> restored = gson.fromJson(json, new TypeToken<List<User>>(){}.getType()); // Gson 2.10+ 更簡潔寫法 // List<User> restored = gson.fromJson(json, TypeToken.getParameterized(List.class, User.class));

場景三:陣列

User[] array = {user1, user2}; String json = gson.toJson(array); User[] restored = gson.fromJson(json, new TypeToken<User[]>(){}.getType());

場景四:Map (Key 為 String/自定義對象)

// String Key Map<String, User> map = new LinkedHashMap<>(); map.put("u1", user1); map.put("u2", user2); String json = gson.toJson(map); Map<String, User> restored = gson.fromJson(json, new TypeToken<Map<String, User>>(){}.getType()); // 自定義 Key (Point) 需開啟 enableComplexMapKeySerialization() Gson gson2 = new GsonBuilder().enableComplexMapKeySerialization().create(); Map<Point, User> complexMap = new LinkedHashMap<>(); complexMap.put(new Point(1,1), user1); String json2 = gson2.toJson(complexMap); Map<Point, User> restored2 = gson2.fromJson(json2, new TypeToken<Map<Point, User>>(){}.getType());

場景五:巢狀泛型

Map<String, List<User>> nested = new LinkedHashMap<>(); nested.put("teamA", Arrays.asList(user1, user2)); String json = gson.toJson(nested); Map<String, List<User>> restored = gson.fromJson(json, new TypeToken<Map<String, List<User>>>(){}.getType());

場景六:日期、BigDecimal、枚舉客製化 (GsonBuilder)

Gson gson = new GsonBuilder() .setDateFormat("yyyy-MM-dd HH:mm:ss") // 統一日期格式 .registerTypeAdapter(BigDecimal.class, new TypeAdapter<BigDecimal>() { @Override public void write(JsonWriter out, BigDecimal v) throws IOException { out.value(v.stripTrailingZeros().toPlainString()); // 避免科學記號 } @Override public BigDecimal read(JsonReader in) throws IOException { return new BigDecimal(in.nextString()); } }) .registerTypeAdapter(User.Status.class, new TypeAdapter<User.Status>() { @Override public void write(JsonWriter out, User.Status v) throws IOException { out.value(v.name()); // 枚舉輸出名稱而非 ordinal } @Override public User.Status read(JsonReader in) throws IOException { return User.Status.valueOf(in.nextString()); } }) .disableHtmlEscaping() // 保留 < > & 字符 .create();

場景七:欄位級控制 (註解)

public class User { @SerializedName("user_name") // JSON Key 重命名 private String name; @Expose(serialize = false, deserialize = false) // 完全忽略 private String password; @Expose(serialize = false) // 僅反序列化,不序列化 (如只讀欄位) private Long creatorId; transient private String tempToken; // transient 同樣忽略 // 需配合 .excludeFieldsWithoutExposeAnnotation() 啟用 @Expose 控制 }

場景八:流式處理大數據 (JsonReader/JsonWriter)

// 寫入大量對象到檔案,避免 OOM try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(new FileOutputStream("users.json"), StandardCharsets.UTF_8))) { writer.setIndent("  "); writer.beginArray(); for (User u : hugeUserList) { gson.toJson(u, User.class, writer); } writer.endArray(); } // 讀取大量對象,流式處理 try (JsonReader reader = new JsonReader(new InputStreamReader(new FileInputStream("users.json"), StandardCharsets.UTF_8))) { reader.beginArray(); while (reader.hasNext()) { User u = gson.fromJson(reader, User.class); process(u); } reader.endArray(); }

完整 API 速查表

場景序列化反序列化關鍵點
Beangson.toJson(obj)gson.fromJson(json, Clazz.class)無需 TypeToken
List<T>gson.toJson(list)gson.fromJson(json, new TypeToken<List<T>>(){}.getType())TypeToken 必須
Set<T>同 ListTypeToken<Set<T>>同 List
T[]gson.toJson(array)TypeToken<T[]>陣列同理
Map<K,V>gson.toJson(map)TypeToken<Map<K,V>>K 為非 String 需 enableComplexMapKeySerialization
巢狀泛型gson.toJson(obj)TypeToken 捕獲完整結構層層嵌套 TypeToken
日期自動 ISO-8601自動解析 ISO-8601建議 setDateFormat 統一格式
BigDecimal科學記號字符串解析註冊 TypeAdapter 輸出純數字
枚舉ordinal (數字)ordinal 解析註冊 TypeAdapter 輸出 name()
循環引用StackOverflowError同@Expose 控制或改用 Jackson

最佳實踐總結

  1. 統一 Gson 實例:單例模式,配置 GsonBuilder 一次建立,全局復用。
  2. 封裝 JsonUtils:業務代碼僅調用 JsonUtils.toJson()/fromJson(),隔離 Gson 細節。
  3. 強制 TypeToken:所有泛型集合反序列化必須使用 TypeToken,代碼審查重點檢查此點。
  4. 日期格式統一:專案層面統一 yyyy-MM-dd HH:mm:ss,避免跨系統對接亂碼。
  5. BigDecimal 必註冊 Adapter:防止科學記號導致精度誤解、金融計算錯誤。
  6. 枚舉輸出 name():可讀性、穩定性遠優於 ordinal。
  7. 大數據流式處理:超過 10MB JSON 必須用 JsonReader/Writer,嚴禁全量載入記憶體。
  8. 遷移 Jackson 參考:@JsonProperty↔@SerializedName、@JsonIgnore↔transient/@Expose、ObjectMapper↔Gson,遷移成本極低。

總結

Gson 以極簡 API 覆蓋 95% JSON 處理場景:
✅ toJson/fromJson 雙雄
✅ TypeToken 破解泛型擦除
✅ GsonBuilder 客製化日期/數值/枚舉/欄位控制
✅ JsonReader/Writer 流式處理大數據
掌握本文八大場景與速查表,即可在專案中自信、正確、高效地處理所有 JSON 轉換需求。將此文加入團隊知識庫,新成員上手即專家級水準。

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