Lớp JsonNode trong Jackson cung cấp mô hình cây (Tree Model) để thao tác JSON mà không cần định nghĩa trước cấu trúc. Đây là lựa chọn lý tưởng khi làm việc với dữ liệu JSON linh hoạt, không cố định hoặc có độ lồng ghép phức tạp.
Các loại node cơ bản
- ObjectNode: Đại diện cho đối tượng JSON
{}, hỗ trợ thêm/sửa/xóa thuộc tính. - ArrayNode: Đại diện cho mảng JSON
[], hỗ trợ thêm/xóa/phần tử. - TextNode, NumericNode, BooleanNode, NullNode: Đại diện cho các giá trị nguyên thủy.
Mọi thao tác đọc đều thực hiện qua giao diện
JsonNode. Chỉ khi cần chỉnh sửa, bạn mới ép kiểu sangObjectNodehoặcArrayNode.
Thiết lập Maven
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
Phân tích JSON thành cây
ObjectMapper om = new ObjectMapper();
String input = """
{
"username": "minh",
"score": 85,
"premium": true,
"skills": ["Java", "Spring"],
"profile": {"region": "Hanoi", "code": "10000"}
}
""";
JsonNode tree = om.readTree(input);
Truy cập an toàn dữ liệu
// Truy cập thuộc tính
String user = tree.get("username").asText("guest");
int point = tree.get("score").asInt(0);
boolean vip = tree.get("premium").asBoolean();
// Duyệt mảng
JsonNode skills = tree.get("skills");
if (skills.isArray()) {
for (JsonNode skill : skills) {
System.out.println("- " + skill.asText());
}
}
// Truy cập lồng
String region = tree.get("profile").get("region").asText();
Chỉnh sửa cấu trúc JSON
// Chuyển đổi để chỉnh sửa
ObjectNode modifiable = (ObjectNode) tree;
// Thay đổi giá trị
modifiable.put("username", "minh_pro");
modifiable.put("level", 5);
// Xóa thuộc tính
modifiable.remove("score");
// Thêm mảng mới
ArrayNode tools = modifiable.putArray("tools");
tools.add("VSCode").add("Docker");
// Thêm đối tượng con
ObjectNode settings = modifiable.putObject("settings");
settings.put("darkMode", true).put("notifications", false);
Tạo JSON từ đầu
ObjectMapper om = new ObjectMapper();
ObjectNode root = om.createObjectNode();
root.put("projectId", "PRJ-2024");
root.put("status", "active");
ArrayNode members = root.putArray("team");
members.add("Alice").add("Bob");
ObjectNode metadata = root.putObject("meta");
metadata.put("createdAt", "2024-06-01").put("owner", "admin");
String result = om.writeValueAsString(root);
System.out.println(result);
Tra cứu theo đường dẫn
JsonNode location = tree.at("/profile/region");
if (!location.isMissingNode()) {
System.out.println("Vị trí: " + location.asText());
}
JsonNode firstSkill = tree.at("/skills/0");
System.out.println("Kỹ năng đầu: " + firstSkill.asText());
Chuyển đổi với POJO
// JsonNode → Object
Project proj = om.treeToValue(tree, Project.class);
// Object → JsonNode
JsonNode fromPojo = om.valueToTree(proj);
Sử dụng trong Spring Boot
@PostMapping("/process")
public ResponseEntity<String> handleDynamic(@RequestBody JsonNode data) {
String action = data.get("action").asText();
int userId = data.get("userId").asInt();
// Xử lý logic tùy biến...
return ResponseEntity.ok("Đã xử lý");
}
@GetMapping("/dynamic-config")
public JsonNode getConfig() {
ObjectNode config = om.createObjectNode();
config.put("maxRetries", 3);
config.put("timeoutMs", 10000);
return config;
}
Thực hành tốt nhất
| Vấn đề | Giải pháp |
|---|---|
| Lỗi NullPointerException | Kiểm tra null hoặc dùng at() |
| Ép kiểu sai | Dùng isXxx() trước khi gọi asXxx() |
| Không thể sửa node | Ép kiểu sang ObjectNode/ArrayNode |
| Hiệu năng kém với JSON lớn | Chuyển sang JsonParser stream |
Khi nào nên dùng JsonNode?
| Trường hợp | Khuyến nghị |
|---|---|
| Cấu trúc JSON cố định | Dùng POJO với @JsonProperty |
| JSON linh hoạt, không xác định trước | Dùng JsonNode |
| Cần chỉnh sửa một phần JSON | JsonNode |
| Yêu cầu hiệu năng cao | Dùng JsonParser stream |
| Cần kiểm tra kiểu tại compile-time | POJO |