為何選 Jackson?
- 效能極高 (流式解析、零拷貝、字節碼生成)。
- 功能全:註解驅動、多態、泛型、XML/YAML/CBOR/Protobuf 擴展。
- 生態成熟:Spring Boot 預設、Logstash、Kafka Connect、Elasticsearch 內建。
核心類:ObjectMapper
ObjectMapper mapper = new ObjectMapper();
// 常用配置
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // 日期輸出 ISO-8601
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略多餘欄位
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 自定義日期格式
mapper.registerModule(new JavaTimeModule()); // Java 8 LocalDateTime 支援
場景一:POJO 序列化
public class Student {
private int age = 10;
private String name = "HHH";
private String[] tags = {"hao", "haouhao"};
private LocalDateTime time = LocalDateTime.now();
// getter/setter...
}
Student s = new Student();
String json = mapper.writeValueAsString(s);
// {"tags":["hao","haouhao"],"time":"2023-08-02 03:48:20","name":"HHH","age":10}
場景二:反序列化
1. 完整綁定 (Data Binding)
Student s = mapper.readValue(jsonString, Student.class);
2. 樹模型
JsonNode root = mapper.readTree(jsonString);
int age = root.get("age").asInt();
JsonNode tags = root.get("tags");
for (JsonNode t : tags) System.out.println(t.asText());
3. 流式 API (高效能、低記憶體)
try (JsonParser parser = mapper.getFactory().createParser(jsonString)) {
while (parser.nextToken() != JsonToken.END_OBJECT) {
if (parser.currentName().equals("age")) {
parser.nextToken(); // 移至 VALUE_NUMBER_INT
System.out.println("Age: " + parser.getIntValue());
}
}
}
場景三:Map/Collection 綁定
// JSON → Map
Map map = mapper.readValue(json, new TypeReference
進階:註解控制
| 註解 | 用途 |
@JsonProperty("name") | 指定 JSON 欄位名 |
@JsonIgnore | 排除欄位 |
@JsonFormat(pattern="yyyy-MM-dd") | 日期格式化 |
@JsonInclude(Include.NON_NULL) | 忽略 null 值 |
@JsonSubTypes / @JsonTypeInfo | 多態類型資訊 |
@JsonCreator / @JsonProperty | 構造函數/工廠方法反序列化 |
效能優化建議
- 複用 ObjectMapper:線程安全,單例模式。
- 啟用 Afterburner:`mapper.enable(SerializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)` 等,字節碼生成加速。
- 避免大對象樹模型:百萬級數組用流式 API 或分頁處理。
- 日期用 Java 8 `java.time`:不可變、線程安全、無需 `SimpleDateFormat` 同步鎖。