Java JAXB
介绍
JAXB (Java Architecture for XML Binding) 是Java提供的一种用于XML数据绑定的技术。它允许开发者将Java对象转换为XML文档,或者将XML文档转换为Java对象。这种双向转换大大简化了Java应用程序处理XML数据的过程,使开发者无需深入了解XML解析的复杂细节。
JAXB主要提供两个核心功能:
- 编组(Marshal):将Java对象转换为XML文档
- 解组(Unmarshal):将XML文档转换为Java对象
备注
JAXB从Java 6开始被包含在Java标准库中,在Java 11之前是Java SE的一部分。从Java 11开始,它被移出核心JDK,需要作为单独的依赖引入。
JAXB基础概念
在使用JAXB之前,我们需要了解几个关键注解:
- @XmlRootElement: 标记Java类作为XML根元素
- @XmlElement: 标记字段或属性作为XML元素
- @XmlAttribute: 标记字段或属性作为XML属性
- @XmlAccessorType: 控制JAXB如何访问类中的字段
- @XmlType: 定义XML Schema类型
入门示例
步骤1:创建Java类并添加JAXB注解
import jakarta.xml.bind.annotation.*;
@XmlRootElement(name = "student")
@XmlAccessorType(XmlAccessType.FIELD)
public class Student {
@XmlAttribute
private int id;
@XmlElement
private String name;
@XmlElement
private int age;
// 默认构造函数(JAXB需要)
public Student() {}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// Getters and Setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
警告
JAXB需要一个无参构造函数来创建对象实例,因此在使用JAXB的类中务必提供默认构造函数。
步骤2:Java对象转换为XML(编组/Marshal)
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import java.io.File;
public class JaxbMarshalExample {
public static void main(String[] args) {
try {
// 创建JAXBContext对象
JAXBContext context = JAXBContext.newInstance(Student.class);
// 创建Marshaller对象
Marshaller marshaller = context.createMarshaller();
// 设置格式化输出
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// 创建Student对象
Student student = new Student(1, "张三", 20);
// 将Java对象转换为XML并输出到控制台
System.out.println("输出到控制台:");
marshaller.marshal(student, System.out);
// 将Java对象转换为XML并保存到文件
File file = new File("student.xml");
marshaller.marshal(student, file);
System.out.println("\nXML已保存到: " + file.getAbsolutePath());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
输出结果:
输出到控制台:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student id="1">
<name>张三</name>
<age>20</age>
</student>
XML已保存到: /path/to/student.xml
步骤3:XML转换为Java对象(解组/Unmarshal)
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import java.io.File;
public class JaxbUnmarshalExample {
public static void main(String[] args) {
try {
// 创建JAXBContext对象
JAXBContext context = JAXBContext.newInstance(Student.class);
// 创建Unmarshaller对象
Unmarshaller unmarshaller = context.createUnmarshaller();
// 从XML文件读取并转换为Java对象
File file = new File("student.xml");
Student student = (Student) unmarshaller.unmarshal(file);
// 输出转换后的Java对象
System.out.println("解析XML后的Student对象: " + student);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
输出结果:
解析XML后的Student对象: Student [id=1, name=张三, age=20]
深入了解JAXB注解
@XmlAccessorType
控制JAXB如何访问类中的字段:
@XmlAccessorType(XmlAccessType.FIELD) // 直接访问字段
@XmlAccessorType(XmlAccessType.PROPERTY) // 通过getter/setter访问
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER) // 访问公共字段和属性(默认)
@XmlAccessorType(XmlAccessType.NONE) // 不自动访问任何字段或属性
@XmlTransient
用于排除不需要转换为XML的字段:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String name;
@XmlTransient
private String password; // 此字段不 会包含在XML中
}
@XmlElementWrapper
用于生成集合周围的包装元素:
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Course {
private String courseName;
@XmlElementWrapper(name = "students")
@XmlElement(name = "student")
private List<Student> studentList;
}
生成的XML:
<course>
<courseName>Java编程</courseName>
<students>
<student>...</student>
<student>...</student>
</students>
</course>
实际应用案例:学生成绩管理系统
让我们来看一个更复杂的例子,展示如何使用JAXB处理多层嵌套的对象关系。
步骤1:定义数据模型
@XmlRootElement(name = "school")
@XmlAccessorType(XmlAccessType.FIELD)
public class School {
@XmlAttribute
private String name;
@XmlElementWrapper(name = "classes")
@XmlElement(name = "class")
private List<ClassRoom> classes;
// 构造函数、getter和setter
}
@XmlAccessorType(XmlAccessType.FIELD)
public class ClassRoom {
@XmlAttribute
private String name;
@XmlElementWrapper(name = "students")
@XmlElement(name = "student")
private List<Student> students;
// 构造函数、getter和setter
}
@XmlAccessorType(XmlAccessType.FIELD)
public class Student {
@XmlAttribute
private int id;
@XmlElement
private String name;
@XmlElementWrapper(name = "scores")
@XmlElement(name = "subject")
private Map<String, Integer> subjectScores;
// 构造函数、getter和setter
}
步骤2:创建数据并转换为XML
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import java.io.File;
import java.util.*;
public class SchoolManagementSystem {
public static void main(String[] args) {
try {
// 创建学生和分数
Map<String, Integer> scores1 = new HashMap<>();
scores1.put("数学", 95);
scores1.put("语文", 88);
scores1.put("英语", 92);
Map<String, Integer> scores2 = new HashMap<>();
scores2.put("数学", 78);
scores2.put("语文", 90);
scores2.put("英语", 85);
// 创建学生
Student student1 = new Student(1, "李明", scores1);
Student student2 = new Student(2, "王红", scores2);
// 创建班级并添加学生
ClassRoom classRoom = new ClassRoom();
classRoom.setName("一年级(1)班");
classRoom.setStudents(Arrays.asList(student1, student2));
// 创建学校并添加班级
School school = new School();
school.setName("实验小学");
school.setClasses(Collections.singletonList(classRoom));
// 创建JAXBContext
JAXBContext context = JAXBContext.newInstance(School.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// 将对象转换为XML并保存
File file = new File("school.xml");
marshaller.marshal(school, file);
System.out.println("学校数据已保存到: " + file.getAbsolutePath());
// 输出到控制台
marshaller.marshal(school, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
输出结果:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<school name="实验小学">
<classes>
<class name="一年级(1)班">
<students>
<student id="1">
<name>李明</name>
<scores>
<subject key="数学">95</subject>
<subject key="语文">88</subject>
<subject key="英语">92</subject>
</scores>
</student>
<student id="2">
<name>王红</name>
<scores>
<subject key="数学">78</subject>
<subject key="语文">90</subject>
<subject key="英语">85</subject>
</scores>
</student>
</students>
</class>
</classes>
</school>
步骤3:从XML读取数据
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import java.io.File;
public class ReadSchoolData {
public static void main(String[] args) {
try {
// 创建JAXBContext
JAXBContext context = JAXBContext.newInstance(School.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
// 从XML文件读取学校数据
File file = new File("school.xml");
School school = (School) unmarshaller.unmarshal(file);
// 打印学校信息
System.out.println("学校名称: " + school.getName());
// 遍历班级
for (ClassRoom classRoom : school.getClasses()) {
System.out.println("班级: " + classRoom.getName());
// 遍历学生
for (Student student : classRoom.getStudents()) {
System.out.println(" 学生: " + student.getId() + " - " + student.getName());
// 打印成绩
System.out.println(" 成绩:");
for (Map.Entry<String, Integer> entry : student.getSubjectScores().entrySet()) {
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
}
}
}
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
输出结果:
学校名称: 实验小学
班级: 一年级(1)班
学生: 1 - 李明
成绩:
数学: 95
语文: 88
英语: 92
学生: 2 - 王红
成绩:
数学: 78
语文: 90
英语: 85
自定义适配器
当需要处理特殊数据类型时,可以使用JAXB适配器来自定义转换逻辑。
例如,假设我们要处理日期格式:
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateAdapter extends XmlAdapter<String, Date> {
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Override
public Date unmarshal(String v) throws Exception {
return dateFormat.parse(v);
}
@Override
public String marshal(Date v) throws Exception {
return dateFormat.format(v);
}
}