Hướng dẫn chi tiết về chuyển đổi đối tượng Java và XML sử dụng JAXB

JAXB (Java Architecture for XML Binding) là một chuẩn công nghệ trong nền tảng Java, cho phép các nhà phát triển ánh xạ giữa các lớp Java và các biểu diễn XML. Cơ chế này hỗ trợ hai chiều: marshalling (chuyển đổi đối tượng Java thành XML) và unmarshalling (phân tích XML trở lại thành đối tượng Java). Bài viết này sẽ trình bày cách áp dụng JAXB thông qua các ví dụ từ cơ bản đến nâng cao.

1. Thực hành cơ bản với JAXB

Để bắt đầu, chúng ta cần định nghĩa một lớp thực thể (Entity) đại diện cho dữ liệu cần chuyển đổi. Trong ví dụ này, chúng ta sẽ sử dụng lớp Employee (Nhân viên).

1.1 Định nghĩa lớp thực thể

Lớp Employee được đánh dấu bằng annotation @XmlRootElement để chỉ định nó là gốc của cây XML. Các thuộc tính bên trong sẽ tự động được ánh xạ thành các thẻ con.

package com.example.jaxb.model;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "employee")
public class Employee implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    private String employeeId;
    private String fullName;
    private String department;
    private Double salary;

    // Các phương thức Getter và Setter
    public String getEmployeeId() {
        return employeeId;
    }

    public void setEmployeeId(String employeeId) {
        this.employeeId = employeeId;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [ID=" + employeeId + ", Name=" + fullName + 
               ", Dept=" + department + ", Salary=" + salary + "]";
    }
}

1.2 Viết lớp kiểm thử (Test Class)

Tiến trình thực hiện bao gồm việc khởi tạo JAXBContext, tạo đối tượng Marshaller để xuất dữ liệu ra XML, và Unmarshaller để đọc XML vào đối tượng. Chúng ta sẽ sử dụng StringWriterStringReader để xử lý dữ liệu dưới dạng chuỗi.

package com.example.jaxb.test;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;

public class JAXBTestRunner {

    public static void main(String[] args) {
        try {
            // 1. Khởi tạo đối tượng
            Employee emp = new Employee();
            emp.setEmployeeId("EMP001");
            emp.setFullName("Nguyen Van A");
            emp.setDepartment("Engineering");
            emp.setSalary(1500.50);

            // 2. Tạo JAXB Context
            JAXBContext jaxbContext = JAXBContext.newInstance(Employee.class);

            // 3. Marshalling: Java Object -> XML
            Marshaller marshaller = jaxbContext.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            
            StringWriter writer = new StringWriter();
            marshaller.marshal(emp, writer);
            
            String xmlOutput = writer.toString();
            System.out.println("--- Kết quả XML sau khi Marshalling ---");
            System.out.println(xmlOutput);

            // 4. Unmarshalling: XML -> Java Object
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            StringReader reader = new StringReader(xmlOutput);
            
            Employee empCopy = (Employee) unmarshaller.unmarshal(reader);
            System.out.println("\n--- Đối tượng sau khi Unmarshalling ---");
            System.out.println(empCopy);

        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

1.3 Kết quả đầu ra

Khi thực thi chương trình, output thu được sẽ có cấu trúc như sau:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
    <department>Engineering</department>
    <employeeId>EMP001</employeeId>
    <fullName>Nguyen Van A</fullName>
    <salary>1500.5</salary>
</employee>

2. Xây dựng lớp tiện ích (Utility Class)

Để tái sử dụng mã nguồn và quản lý tốt hơn, chúng ta nên đóng gói logic chuyển đổi vào một lớp tiện ích riêng biệt. Lớp này sẽ hỗ trợ generic type để xử lý nhiều loại đối tượng khác nhau.

2.1 Lớp JAXBHelper

package com.example.jaxb.util;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;

public class JAXBHelper {

    /**
     * Chuyển đổi đối tượng Java thành chuỗi XML
     */
    public static <T> String convertToXml(T object) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(object.getClass());
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        
        StringWriter writer = new StringWriter();
        marshaller.marshal(object, writer);
        return writer.toString();
    }

    /**
     * Chuyển đổi chuỗi XML thành đối tượng Java
     */
    @SuppressWarnings("unchecked")
    public static <T> T convertFromXml(String xml, Class<T> clazz) throws JAXBException {
        JAXBContext context = JAXBContext.newInstance(clazz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        return (T) unmarshaller.unmarshal(new StringReader(xml));
    }
}

3. Xử lý cấu trúc XML phức tạp và Kế thừa

Trong thực tế, cấu trúc XML thường phức tạp hơn, bao gồm danh sách các đối tượng hoặc sử dụng tính kế thừa. Giả sử chúng ta cần xây dựng một file XML quản lý kho hàng với cấu trúc: một thẻ gốc Inventory, một thẻ thông tin Info, và danh sách các Item.

3.1 Lớp gốc Inventory

Lớp này chứa thuộc tính để chứa danh sách các mặt hàng. Chúng ta sử dụng @XmlElementRef để hỗ trợ tính đa hình (polymorphism) khi danh sách chứa các kiểu con khác nhau.

package com.example.jaxb.model;

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlRootElement(name = "inventory")
@XmlAccessorType(XmlAccessType.FIELD)
public class Inventory {
    
    @XmlAttribute
    private String warehouseId;

    @XmlElement(name = "info")
    private InventoryInfo info;

    @XmlElementRef
    private List<Item> items;

    // Constructors, Getters, Setters
    public Inventory() {}

    public Inventory(String warehouseId) {
        this.warehouseId = warehouseId;
    }

    public void setInfo(InventoryInfo info) { this.info = info; }
    public void setItems(List<Item> items) { this.items = items; }
}

3.2 Lớp thông tin InventoryInfo

Lớp này minh họa việc sử dụng @XmlValue để nội dung văn bản nằm trực tiếp bên trong thẻ, thay vì một thẻ con.

package com.example.jaxb.model;

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "info")
public class InventoryInfo {
    
    @XmlAttribute
    private String category;

    @XmlValue
    private String description;

    public void setCategory(String category) { this.category = category; }
    public void setDescription(String description) { this.description = description; }
}

3.3 Mô hình kế thừa cho Item

Chúng ta tạo một lớp cha Item và hai lớp con là ElectronicItemBookItem. Sử dụng @XmlSeeAlso ở lớp cha để thông báo cho JAXB về sự tồn tại của các lớp con.

// Lớp cha Item
package com.example.jaxb.model;

import javax.xml.bind.annotation.*;

@XmlSeeAlso({ElectronicItem.class, BookItem.class})
public abstract class Item {
    @XmlAttribute
    protected String sku; // Mã sản phẩm
    
    public void setSku(String sku) { this.sku = sku; }
}

// Lớp con ElectronicItem
package com.example.jaxb.model;

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "electronic")
public class ElectronicItem extends Item {
    @XmlAttribute
    private String brand;

    public void setBrand(String brand) { this.brand = brand; }
}

// Lớp con BookItem
package com.example.jaxb.model;

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "book")
public class BookItem extends Item {
    @XmlAttribute
    private String title;

    public void setTitle(String title) { this.title = title; }
}

3.4 Chạy thử nghiệm

Đoạn mã sau sẽ tạo ra một cấu trúc XML chứa các loại mặt hàng khác nhau trong cùng một danh sách.

package com.example.jaxb.test;

import com.example.jaxb.model.*;
import com.example.jaxb.util.JAXBHelper;
import java.util.ArrayList;
import java.util.List;

public class ComplexModelTest {

    public static void main(String[] args) {
        try {
            Inventory inventory = new Inventory("WH-01");
            
            InventoryInfo info = new InventoryInfo();
            info.setCategory("General");
            info.setDescription("Danh sách hàng tồn kho tháng 1");
            inventory.setInfo(info);

            List<Item> itemList = new ArrayList<>();
            
            ElectronicItem laptop = new ElectronicItem();
            laptop.setSku("E-100");
            laptop.setBrand("Dell");
            itemList.add(laptop);

            BookItem novel = new BookItem();
            novel.setSku("B-200");
            novel.setTitle("Java Programming Guide");
            itemList.add(novel);

            inventory.setItems(itemList);

            String xml = JAXBHelper.convertToXml(inventory);
            System.out.println(xml);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Kết quả XML sinh ra sẽ tự động phân biệt loại thẻ (<electronic><book>) dựa trên kiểu đối tượng thực tế trong danh sách:

<inventory warehouseId="WH-01">
    <info category="General">Danh sách hàng tồn kho tháng 1</info>
    <electronic sku="E-100" brand="Dell"/>
    <book sku="B-200" title="Java Programming Guide"/>
</inventory>

Thẻ: jaxb Java XML Serialization Object Mapping

Đăng vào ngày 14 tháng 8 lúc 20:34