Jackson 是 Java 生態系中最主流的 JSON 處理庫,提供三種核心處理模式:資料綁定、樹模型、流式 API。本文重點解析資料綁定與樹模型的實戰應用,並提供完整可執行範例。
Maven 依賴 (Jackson 1.x 版本,現代專案建議升級至 2.x):
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
ObjectMapper 是 Jackson 的核心進入點,線程安全且可重複使用,建議作為單例管理:
// 單例模式最佳實踐
public class JacksonUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.enable(SerializationFeature.INDENT_OUTPUT); // 格式化輸出
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略未知屬性
}
public static ObjectMapper getMapper() { return MAPPER; }
}
public class Student {
private String name;
private int age;
// Getter/Setter/建構子/toString 省略
}
ObjectMapper mapper = JacksonUtil.getMapper();
// JSON → POJO
String json = "{\"name\":\"Mahesh\", \"age\":21}";
Student student = mapper.readValue(json, Student.class);
System.out.println(student); // Student [name: Mahesh, age: 21]
// POJO → JSON
String output = mapper.writeValueAsString(student);
System.out.println(output);
// 輸出:
// {
// "name" : "Mahesh",
// "age" : 21
// }
樹模型適合結構不固定、無對應 POJO、或需部分提取/修改的場景,類似 XML DOM 解析。
String json = "{\"name\":\"Mahesh Kumar\", \"age\":21, \"verified\":false, \"marks\":[100,90,85]}";
JsonNode rootNode = mapper.readTree(json);
// 簡單欄位
String name = rootNode.path("name").asText(); // "Mahesh Kumar"
int age = rootNode.path("age").asInt(); // 21
boolean verified = rootNode.path("verified").asBoolean(); // false
// 陣列節點遍歷
JsonNode marksNode = rootNode.path("marks");
for (JsonNode mark : marksNode) {
System.out.print(mark.asInt() + " "); // 100 90 85
}
// 安全導航:節點不存在時返回 MissingNode,不拋異常
String missing = rootNode.path("address").asText("N/A"); // "N/A"
ObjectNode root = mapper.createObjectNode();
ArrayNode marks = mapper.createArrayNode();
marks.add(100).add(90).add(85);
root.put("name", "Mahesh Kumar");
root.put("age", 21);
root.put("verified", false);
root.set("marks", marks); // 陣列用 set
// 寫入檔案
mapper.writeValue(new File("student.json"), root);
// JsonNode → POJO (需目標類有預設建構子) Student student = mapper.treeToValue(rootNode, Student.class); // POJO → JsonNode JsonNode node = mapper.valueToTree(student);
// Java 8 LocalDateTime mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 輸出 ISO-8601 字串
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
public class MaskingSerializer extends JsonSerializer{ @Override public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (value != null && value.length() > 4) { gen.writeString(value.substring(0, 2) + "***" + value.substring(value.length() - 2)); } else { gen.writeString(value); } } } // 使用 @JsonSerialize(using = MaskingSerializer.class) private String idNumber;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Dog.class, name = "dog"),
@JsonSubTypes.Type(value = Cat.class, name = "cat")
})
public abstract class Animal { ... }
mapper.registerModule(new AfterburnerModule()) 透過字節碼生成加速存取JsonParser/JsonGenerator 低記憶體佔用mapper.enable(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY)| 問題 | 原因 | 解決 |
|---|---|---|
| 反序列化失敗:No suitable constructor | 缺少無參建構子 | 加上 @JsonCreator 或提供預設建構子 |
| 日期輸出為時間戳 | 預設 WRITE_DATES_AS_TIMESTAMPS=true | disable 該特性並註冊 JavaTimeModule |
| 未知屬性拋異常 | FAIL_ON_UNKNOWN_PROPERTIES=true | configure 為 false |
| 循環引用 StackOverflow | 雙向關係 | @JsonManagedReference/@JsonBackReference 或 @JsonIdentityInfo |