Xử lý JSON động trong Java với Jackson JsonNode

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 sang ObjectNode hoặc ArrayNode.

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 NullPointerExceptionKiểm tra null hoặc dùng at()
Ép kiểu saiDù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ớnChuyển sang JsonParser stream

Khi nào nên dùng JsonNode?

Trường hợpKhuyến nghị
Cấu trúc JSON cố địnhDùng POJO với @JsonProperty
JSON linh hoạt, không xác định trướcDùng JsonNode
Cần chỉnh sửa một phần JSONJsonNode
Yêu cầu hiệu năng caoDùng JsonParser stream
Cần kiểm tra kiểu tại compile-timePOJO

Thẻ: Jackson jsonnode Java json-processing dynamic-json

Đăng vào ngày 5 tháng 6 lúc 16:10