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 } } 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<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()); // 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()); 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 控制 } // 寫入大量對象到檔案,避免 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(); } | 場景 | 序列化 | 反序列化 | 關鍵點 |
|---|---|---|---|
| Bean | gson.toJson(obj) | gson.fromJson(json, Clazz.class) | 無需 TypeToken |
| List<T> | gson.toJson(list) | gson.fromJson(json, new TypeToken<List<T>>(){}.getType()) | TypeToken 必須 |
| Set<T> | 同 List | TypeToken<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 |
GsonBuilder 一次建立,全局復用。JsonUtils.toJson()/fromJson(),隔離 Gson 細節。yyyy-MM-dd HH:mm:ss,避免跨系統對接亂碼。JsonReader/Writer,嚴禁全量載入記憶體。@JsonProperty↔@SerializedName、@JsonIgnore↔transient/@Expose、ObjectMapper↔Gson,遷移成本極低。Gson 以極簡 API 覆蓋 95% JSON 處理場景:
✅ toJson/fromJson 雙雄
✅ TypeToken 破解泛型擦除
✅ GsonBuilder 客製化日期/數值/枚舉/欄位控制
✅ JsonReader/Writer 流式處理大數據
掌握本文八大場景與速查表,即可在專案中自信、正確、高效地處理所有 JSON 轉換需求。將此文加入團隊知識庫,新成員上手即專家級水準。