Hướng dẫn JAXB trong Java: Từ Schema đến Java và Ngược lại

Giới thiệu về JAXB

JAXB (Java Architecture for XML Binding) cung cấp cơ chế chuyển đổi hai chiều giữa đối tượng Java và tài liệu XML. Bài viết này tập trung vào các ví dụ JAXB thực tế, bao gồm thao tác với nội dung XML, tùy chỉnh ánh xạ giữa Schema và Java, cũng như tạo Schema từ các lớp Java có chú thích.

Các ví dụ được tổ chức thành ba nhóm chính:

  • Ví dụ cơ bản: Minh họa giải nén (unmarshal), sửa đổi và đóng gói (marshal) dữ liệu XML.
  • Ví dụ tùy chỉnh: Hướng dẫn điều chỉnh ánh xạ mặc định giữa XML Schema và Java.
  • Ví dụ Java-to-Schema: Chỉ cách tạo XML Schema từ các lớp Java có chú thích.

Thiết lập môi trường

Trước khi bắt đầu, hãy tải và cài đặt gói JAXB RI (Reference Implementation) từ jaxb.java.net. Các tệp mẫu nằm trong thư mục jaxb-ri-install/samples/.

Ví dụ cơ bản

1. Sửa đổi và đóng gói (Modify Marshal)

Ví dụ này minh họa cách thay đổi nội dung của cây đối tượng Java và đóng gói lại thành XML.

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import primer.po.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // 1. Tạo context cho package primer.po
        JAXBContext jc = JAXBContext.newInstance("primer.po");

        // 2. Giải nén file XML thành đối tượng Java
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        PurchaseOrder po = (PurchaseOrder) unmarshaller.unmarshal(new FileInputStream("po.xml"));

        // 3. Sửa đổi thông tin địa chỉ thanh toán
        USAddress address = po.getBillTo();
        address.setName("John Bob");
        address.setStreet("242 Main Street");
        address.setCity("Beverly Hills");
        address.setState("CA");
        address.setZip(new BigDecimal("90210"));

        // 4. Đóng gói đối tượng Java trở lại XML
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(po, System.out);
    }
}

2. Giải nén và xác thực (Unmarshal Validate)

Ví dụ này cho thấy cách kích hoạt tính năng xác thực trong quá trình giải nén XML.

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.ValidationEventLocator;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import primer.po.*;
import org.xml.sax.SAXException;

public class Main {
    public static void main(String[] args) throws Exception {
        // 1. Tạo JAXB context
        JAXBContext jc = JAXBContext.newInstance("primer.po");

        // 2. Tạo Unmarshaller và bật chế độ xác thực
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        // 3. Thiết lập Schema để xác thực
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(new File("po.xsd"));
        unmarshaller.setSchema(schema);

        // 4. Thử giải nén file po.xml (có thể chứa lỗi cố ý)
        try {
            PurchaseOrder po = (PurchaseOrder) unmarshaller.unmarshal(
                    new FileInputStream("po.xml"));
            System.out.println("Giải nén thành công!");
        } catch (UnmarshalException ue) {
            System.out.println("Lỗi xác thực: " + ue.getMessage());
        } catch (JAXBException je) {
            je.printStackTrace();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

Trình biên dịch JAXB (xjc)

Trình biên dịch xjc chuyển đổi XML Schema thành các lớp Java. Cú pháp cơ bản:

xjc [-options ...] <schema-file>... [-b >bindinfo<]...

Các tùy chọn quan trọng:

  • -nv: Bỏ qua kiểm tra schema chặt chẽ.
  • -extension: Cho phép sử dụng extension của JAXB.
  • -b file: Chỉ định tệp ràng buộc bên ngoài.
  • -d dir: Thư mục đầu ra cho các lớp Java.
  • -p package: Tên package cho các lớp được sinh ra.
  • -verbose: Hiển thị thông tin chi tiết trong quá trình biên dịch.

Trình tạo Schema (schemagen)

schemagen tạo XML Schema từ các lớp Java có chú thích JAXB.

schemagen [-d path] [java-source-files]

Tùy chỉnh ánh xạ Schema - Java

JAXB cho phép tùy chỉnh ánh xạ mặc định thông qua hai cơ chế:

  • Inline: Khai báo trực tiếp trong tệp XML Schema.
  • External: Sử dụng tệp ràng buộc bên ngoài (thường có đuôi .xjb).

Các loại khai báo tùy chỉnh

Khai báo Phạm vi Mô tả
<globalBindings> Toàn cục Ảnh hưởng đến toàn bộ schema.
<schemaBindings> Schema Xác định package và quy tắc đặt tên.
<class> Lớp Tùy chỉnh tên lớp Java hoặc lớp cài đặt.
<property> Thuộc tính Điều chỉnh cách ánh xạ thuộc tính.
<javaType> Kiểu dữ liệu Ghi đè chuyển đổi kiểu XML-Java.
<typesafeEnumClass> Enum Ánh xạ simpleType thành enum.
<javadoc> Tài liệu Thêm Javadoc tùy chỉnh.

Ví dụ tùy chỉnh Inline

Trong tệp po.xsd, chúng ta thêm các khai báo tùy chỉnh:

<xsd:schema 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    jxb:version="1.0">

    <xsd:annotation>
        <xsd:appinfo>
            <jxb:globalBindings
                fixedAttributeAsConstantProperty="true"
                collectionType="java.util.Vector"
                typesafeEnumMemberName="generateError"/>
            <jxb:schemaBindings>
                <jxb:package name="primer.myPo"/>
                <jxb:nameXmlTransform>
                    <jxb:elementName suffix="Element"/>
                </jxb:nameXmlTransform>
            </jxb:schemaBindings>
        </xsd:appinfo>
    </xsd:annotation>

    <xsd:complexType name="PurchaseOrderType">
        <xsd:annotation>
            <xsd:appinfo>
                <jxb:class name="POType">
                    <jxb:javadoc>
                        A &lt;b&gt;Purchase Order&lt;/b&gt; consists of addresses and items.
                    </jxb:javadoc>
                </jxb:class>
            </xsd:appinfo>
        </xsd:annotation>
        <!-- ... -->
    </xsd:complexType>
</xsd:schema>

Ví dụ tùy chỉnh External

Tạo tệp binding.xjb:

<jxb:bindings 
    version="1.0"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
    
    <jxb:bindings schemaLocation="po.xsd" node="/xs:schema">
        <jxb:globalBindings
            fixedAttributeAsConstantProperty="true"
            collectionType="java.util.Vector"/>
        <jxb:schemaBindings>
            <jxb:package name="primer.myPo"/>
        </jxb:schemaBindings>
        
        <jxb:bindings node="//xs:complexType[@name='USAddress']">
            <jxb:class>
                <jxb:javadoc>
                    <![CDATA[First line of documentation for a <b>USAddress</b>.]]>
                </jxb:javadoc>
            </jxb:class>
            <jxb:bindings node=".//xs:element[@name='name']">
                <jxb:property name="toName"/>
            </jxb:bindings>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

Lớp chuyển đổi kiểu dữ liệu tùy chỉnh

package primer;

import java.math.BigInteger;
import javax.xml.bind.DatatypeConverter;

public class MyDatatypeConverter {
    public static short parseIntegerToShort(String value) {
        BigInteger result = DatatypeConverter.parseInteger(value);
        return (short) result.intValue();
    }

    public static String printShortToInteger(short value) {
        BigInteger result = BigInteger.valueOf(value);
        return DatatypeConverter.printInteger(result);
    }
}

Ví dụ Java-to-Schema

Các ví dụ này minh họa cách sử dụng chú thích JAXB để ánh xạ lớp Java sang XML Schema.

j2s-xmlAccessorOrder

Kiểm soát thứ tự các phần tử trong XML bằng @XmlAccessorOrder@XmlType.propOrder.

import javax.xml.bind.annotation.*;

@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
@XmlType(propOrder = {"name", "city"})
public class USAddress {
    private String name;
    private String city;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
}

j2s-xmlAdapter

Sử dụng XmlAdapter để ánh xạ HashMap tùy chỉnh.

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

// Lớp đại diện XML trung gian
class PurchaseListType {
    private List<PartEntry> entry;

    public List<PartEntry> getEntry() { return entry; }
    public void setEntry(List<PartEntry> entry) { this.entry = entry; }
}

class PartEntry {
    @XmlAttribute
    private int key;
    @XmlValue
    private String value;

    // getters, setters...
}

// Adapter chuyển đổi giữa HashMap và PurchaseListType
public class AdapterPurchaseListToHashMap 
    extends XmlAdapter<PurchaseListType, HashMap<Integer, String>> {

    @Override
    public HashMap<Integer, String> unmarshal(PurchaseListType v) throws Exception {
        HashMap<Integer, String> map = new HashMap<>();
        for (PartEntry entry : v.getEntry()) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }

    @Override
    public PurchaseListType marshal(HashMap<Integer, String> v) throws Exception {
        PurchaseListType plt = new PurchaseListType();
        List<PartEntry> entries = new ArrayList<>();
        for (Map.Entry<Integer, String> entry : v.entrySet()) {
            PartEntry pe = new PartEntry();
            pe.setKey(entry.getKey());
            pe.setValue(entry.getValue());
            entries.add(pe);
        }
        plt.setEntry(entries);
        return plt;
    }
}

// Sử dụng trong lớp chính
public class KitchenWorldBasket {
    @XmlJavaTypeAdapter(AdapterPurchaseListToHashMap.class)
    private HashMap<Integer, String> basket;
}

j2s-xmlAttribute

Ánh xạ trường hoặc thuộc tính thành thuộc tính XML.

import javax.xml.bind.annotation.*;

public class USAddress {
    @XmlAttribute
    private String country = "US";

    private String name;
    private String street;

    @XmlAttribute
    public String getState() { return state; }
    public void setState(String state) { this.state = state; }
}

j2s-xmlRootElement

Xác định tên phần tử gốc cho lớp.

import javax.xml.bind.annotation.*;

@XmlRootElement(name = "purchaseOrder")
public class PurchaseOrderType {
    private USAddress shipTo;
    private USAddress billTo;
    // getters, setters...
}

j2s-xmlSchemaType

Tùy chỉnh ánh xạ kiểu dữ liệu XML.

import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;

public class TrackingOrder {
    @XmlSchemaType(name = "date")
    private XMLGregorianCalendar shipDate;

    @XmlSchemaType(name = "gMonthDay")
    private XMLGregorianCalendar orderDate;
}

j2s-xmlType

Ánh xạ lớp thành kiểu phức tạp (complexType) trong Schema.

import javax.xml.bind.annotation.*;

@XmlType(name = "oContext", 
         factoryClass = OrderFormsFactory.class, 
         factoryMethod = "newOrderInstance")
public class OrderContext {
    public OrderContext() { }
}

class OrderFormsFactory {
    public OrderContext newOrderInstance() {
        return new OrderContext();
    }
}

Kết luận

JAXB là một công cụ mạnh mẽ giúp đơn giản hóa việc xử lý XML trong Java. Bằng cách kết hợp các kỹ thuật cơ bản (giải nén, sửa đổi, đóng gói) với các tùy chỉnh linh hoạt, bạn có thể kiểm soát hoàn toàn quá trình chuyển đổi giữa XML và đối tượng Java. Các ví dụ Java-to-Schema mở rộng khả năng này, cho phép bạn bắt đầu từ các lớp Java và tạo ra các Schema tương ứng một cách tự động.

Thẻ: jaxb XML Java Schema Binding Unmarshal

Đăng vào ngày 13 tháng 7 lúc 07:04