HashMap 轉 JSON 看似簡單,但真正用上去踩坑才多。有三種常見的方式,各有優缺點,選對一個才不會被 JsonProcessingException 折磨。
場景說明
先假設有這樣的 HashMap:
1 2 3 4 5 6 7 8 9 10 11
| Map<String, Object> userMap = new HashMap<>(); userMap.put("id", 123); userMap.put("name", "Alice"); userMap.put("email", "alice@example.com"); userMap.put("age", 30); userMap.put("tags", Arrays.asList("admin", "developer"));
Map<String, Object> addressMap = new HashMap<>(); addressMap.put("city", "Taipei"); addressMap.put("country", "Taiwan"); userMap.put("address", addressMap);
|
要轉成 JSON 字串,有三種主流方案。
方案 1:org.json.simple.JSONObject
最直觀的做法,直接 new JSONObject(map):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
import org.json.simple.JSONObject;
Map<String, Object> userMap = new HashMap<>(); userMap.put("id", 123); userMap.put("name", "Alice"); userMap.put("tags", Arrays.asList("admin", "developer"));
String json = new JSONObject(userMap).toJSONString(); System.out.println(json);
|
特點
1 2 3 4 5 6
| Map<String, Object> outer = new HashMap<>(); outer.put("user", userMap); outer.put("timestamp", System.currentTimeMillis());
String json = new JSONObject(outer).toJSONString();
|
優點
- 超直觀 - new JSONObject(map) 一句搞定
- 輕量級 - 依賴小,適合簡單專案
- 不拋 checked exception - toJSONString() 不會拋 JsonProcessingException
缺點
- 功能簡陋 - 沒有格式化、自訂序列化等高級功能
- 效能一般 - 大資料量時比較慢
- 不夠靈活 - null 處理、日期格式化都得自己來
方案 2:Jackson ObjectMapper - 最常用
Jackson 是現在最流行的 JSON 庫,Spring Boot 預設就內建:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
import com.fasterxml.jackson.databind.ObjectMapper;
Map<String, Object> userMap = new HashMap<>(); userMap.put("id", 123); userMap.put("name", "Alice"); userMap.put("tags", Arrays.asList("admin", "developer"));
ObjectMapper mapper = new ObjectMapper(); try { String json = mapper.writeValueAsString(userMap); System.out.println(json); } catch (JsonProcessingException e) { e.printStackTrace(); }
|
推薦做法:複用 ObjectMapper
ObjectMapper 是重的物件,建立成本大。要複用同一個實例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Service public class JsonService { @Autowired private ObjectMapper objectMapper; public String toJson(Map<String, Object> map) throws JsonProcessingException { return objectMapper.writeValueAsString(map); } }
|
格式化輸出
1 2 3 4 5 6 7 8 9
| String json = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(userMap); System.out.println(json);
|
自訂序列化
1 2 3 4 5 6 7 8 9 10 11 12
| ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String json = mapper.writeValueAsString(userMap);
|
優點
- 功能最強 - 支援複雜序列化、自訂轉換器、日期格式等
- 效能最好 - 經過高度最佳化
- 生態完整 - Spring 全面支援,搭配 @JsonProperty、@JsonIgnore 等 annotation
- 廣泛使用 - 業界標準,團隊成員都會用
缺點
- 拋 JsonProcessingException - 必須 catch,或聲明 throws
- 設定複雜 - 各種 mapper 設定容易搞混
- 依賴相對重 - 相比 json-simple
方案 3:Google Gson - 最靈活
Google 出品,在某些場景比 Jackson 更靈活:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonElement;
Map<String, Object> userMap = new HashMap<>(); userMap.put("id", 123); userMap.put("name", "Alice"); userMap.put("tags", Arrays.asList("admin", "developer"));
Gson gson = new Gson();
String json = gson.toJson(userMap); System.out.println(json);
JsonElement jsonElement = gson.toJsonTree(userMap); JsonObject jsonObject = jsonElement.getAsJsonObject(); System.out.println(jsonObject.toString());
|
格式化輸出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Gson gson = new GsonBuilder() .setPrettyPrinting() .create();
String json = gson.toJson(userMap); System.out.println(json);
|
自訂序列化器
1 2 3 4 5 6 7 8 9
| Gson gson = new GsonBuilder() .registerTypeAdapter(LocalDateTime.class, new JsonSerializer<LocalDateTime>() { @Override public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.format(DateTimeFormatter.ISO_DATE_TIME)); } }) .create();
|
優點
- 簡潔 - toJson 方法超直觀,不拋 checked exception
- 靈活 - 可以先 toJsonTree,再修改 JsonObject,最後轉字串
- 適合複雜轉換 - 對於 null、特殊型別的處理比 Jackson 直觀
- 無依賴設定 - 開箱即用
缺點
- 性能稍差 - 比 Jackson 慢一點(但對大多數場景沒影響)
- 業界採用度低 - 不如 Jackson 常見
- 靈活性高導致代碼複雜 - 小任務很簡單,大任務容易亂
三種方案對比
| 項目 |
json-simple |
Jackson |
Gson |
| 使用難度 |
最簡單 |
中等 |
簡單 |
| 功能完整性 |
弱 |
強 |
中等 |
| 效能 |
一般 |
最好 |
中等 |
| 異常處理 |
無 |
JsonProcessingException |
無 |
| 生態支援 |
弱 |
強(Spring 全支援) |
中等 |
| 版本穩定度 |
很舊(2010 年後沒更新) |
最新維護中 |
定期更新 |
| 適合場景 |
簡單專案 |
企業應用、Spring |
複雜轉換、Google 生態 |
實務選擇
推薦優先級
Jackson ObjectMapper(90% 場景)
- Spring Boot 預設就有
- 功能最強、效能最好
- 業界標準
Google Gson(複雜轉換)
- 需要靈活的欄位轉換
- 不想處理 checked exception
json-simple(小型專案、沒其他依賴)
- 專案很小,不想加 Spring 依賴
- 變成過時庫,除非必要別用
實務範例
場景:API response 轉 JSON
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @RestController public class UserController { @Autowired private ObjectMapper objectMapper; @GetMapping("/user/{id}") public String getUser(@PathVariable Long id) throws JsonProcessingException { Map<String, Object> user = new HashMap<>(); user.put("id", id); user.put("name", "Alice"); user.put("email", "alice@example.com"); return objectMapper.writeValueAsString(user); } }
|
場景:嵌套的複雜結構
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Map<String, Object> response = new HashMap<>(); response.put("code", 200); response.put("message", "success");
List<Map<String, Object>> users = new ArrayList<>(); for (int i = 1; i <= 3; i++) { Map<String, Object> user = new HashMap<>(); user.put("id", i); user.put("name", "User" + i); users.add(user); }
response.put("data", users);
ObjectMapper mapper = new ObjectMapper(); String json = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(response);
|
常見踩坑
坑 1:Jackson 沒初始化 ObjectMapper
1 2 3 4 5 6 7 8 9 10
| for (Map map : maps) { String json = new ObjectMapper().writeValueAsString(map); }
ObjectMapper mapper = new ObjectMapper(); for (Map map : maps) { String json = mapper.writeValueAsString(map); }
|
坑 2:Date 序列化亂碼
1 2 3 4 5 6 7 8 9 10
| Map<String, Object> data = new HashMap<>(); data.put("created", new Date()); String json = mapper.writeValueAsString(data);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); String json = mapper.writeValueAsString(data);
|
坑 3:Integer vs Long 精度丟失
1 2 3 4 5
| Map<String, Object> data = new HashMap<>(); data.put("id", 9223372036854775807L); String json = mapper.writeValueAsString(data);
|
最後的話
HashMap 轉 JSON 真的小事一樁,但選對方案事半功倍。90% 時候用 Jackson ObjectMapper 就夠了,記得複用 mapper 實例就行。如果對效能沒要求,json-simple 也可以,但真的變 deprecated 了,不建議新專案用。
你的鼓勵將被轉換為我明天繼續加班的動力(真的)。 ❤️