杰瑞科技汇

Java XML序列化与反序列化如何实现?

XML 序列化是将 Java 对象转换为 XML 格式字符串的过程,而反序列化则是将 XML 字符串转换回 Java 对象的过程,这在配置文件、数据交换、Web 服务等领域有广泛应用。

Java XML序列化与反序列化如何实现?-图1
(图片来源网络,侵删)

Java 提供了多种方式来处理 XML,我将介绍三种最主流的方法:

  1. JAXB (Java Architecture for XML Binding) - (推荐,最常用)
  2. DOM (Document Object Model) - (适用于需要频繁操作 XML 结构的场景)
  3. SAX (Simple API for XML) - (适用于解析大型 XML 文件,内存效率高)

JAXB (Java Architecture for XML Binding)

JAXB 是 Java 标准库的一部分(从 Java 6 开始),它通过注解将 Java 类和 XML 表示绑定在一起,极大地简化了 XML 的处理,它非常适合将整个对象树直接转换为 XML。

核心概念

  • @XmlRootElement: 将一个类映射为 XML 的根元素。
  • @XmlElement: 将一个字段或属性映射为 XML 的一个元素。
  • @XmlAttribute: 将一个字段或属性映射为 XML 元素的属性。
  • @XmlTransient: 标记一个字段或属性,使其不参与 XML 序列化/反序列化。
  • JAXBContext: 入口点,用于管理 XML 绑定信息。
  • Marshaller: 负责将 Java 对象序列化为 XML。
  • Unmarshaller: 负责将 XML 反序列化为 Java 对象。

示例

准备 Java 类

我们创建一个 Java POJO (Plain Old Java Object),并用 JAXB 注解来标记它。

Java XML序列化与反序列化如何实现?-图2
(图片来源网络,侵删)
import javax.xml.bind.annotation.*;
@XmlRootElement(name = "student") // 映射为XML根元素 <student>
@XmlAccessorType(XmlAccessType.FIELD) // 指定使用字段进行绑定,而不是getter/setter
public class Student {
    @XmlAttribute(name = "id") // 映射为XML元素的属性
    private int id;
    @XmlElement(name = "name") // 映射为XML元素 <name>
    private String name;
    @XmlElement(name = "age")
    private int age;
    @XmlElement(name = "course")
    private String course;
    // 必须提供一个无参构造函数,用于反序列化
    public Student() {
    }
    public Student(int id, String name, int age, String course) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.course = course;
    }
    // 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; }
    public String getCourse() { return course; }
    public void setCourse(String course) { this.course = course; }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", course='" + course + '\'' +
                '}';
    }
}

序列化:将 Student 对象转为 XML 字符串

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
public class JaxbSerializationExample {
    public static void main(String[] args) {
        try {
            // 1. 创建一个Student对象
            Student student = new Student(1, "张三", 20, "计算机科学");
            // 2. 创建JAXBContext实例,需要传入要序列化的类的Class对象
            JAXBContext jaxbContext = JAXBContext(Student.class);
            // 3. 创建Marshaller实例
            Marshaller marshaller = jaxbContext.createMarshaller();
            // 4. 设置格式化输出(美化XML)
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            // 5. 将对象序列化到StringWriter中
            StringWriter writer = new StringWriter();
            marshaller.marshal(student, writer);
            // 6. 输出结果
            String xml = writer.toString();
            System.out.println("序列化后的XML:");
            System.out.println(xml);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

序列化后的XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student id="1">
    <name>张三</name>
    <age>20</age>
    <course>计算机科学</course>
</student>

反序列化:将 XML 字符串转为 Student 对象

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class JaxbDeserializationExample {
    public static void main(String[] args) {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" +
                     "<student id=\"1\">\n" +
                     "    <name>张三</name>\n" +
                     "    <age>20</age>\n" +
                     "    <course>计算机科学</course>\n" +
                     "</student>";
        try {
            // 1. 创建JAXBContext实例
            JAXBContext jaxbContext = JAXBContext(Student.class);
            // 2. 创建Unmarshaller实例
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            // 3. 从XML字符串反序列化对象
            // 注意:需要将String转换为InputStream或Reader
            Student student = (Student) unmarshaller.unmarshal(new java.io.StringReader(xml));
            // 4. 输出结果
            System.out.println("反序列化后的对象:");
            System.out.println(student); // 调用对象的toString()方法
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

Java XML序列化与反序列化如何实现?-图3
(图片来源网络,侵删)
反序列化后的对象:
Student{id=1, name='张三', age=20, course='计算机科学'}

DOM (Document Object Model)

DOM 是一种将整个 XML 文档加载到内存中,形成一个树状结构模型的方式,你可以像操作 Java 对象一样自由地遍历、修改、添加或删除这个树中的节点。

特点

  • 易于操作:提供了完整的 API 来访问和修改 XML 数据。
  • 内存消耗大:因为需要将整个文档加载到内存,所以不适合处理非常大的 XML 文件。
  • 随机访问:可以随时访问文档中的任何节点。

示例:解析 XML 并获取数据

import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.IOException;
import org.xml.sax.SAXException;
public class DomParserExample {
    public static void main(String[] args) {
        try {
            // 1. 创建 DocumentBuilderFactory 和 DocumentBuilder
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 2. 解析 XML 文件(这里我们用StringReader模拟)
            String xml = "<student id=\"1\"><name>张三</name><age>20</age><course>计算机科学</course></student>";
            Document document = builder.parse(new java.io.ByteArrayInputStream(xml.getBytes()));
            // 3. 获取根元素
            Element rootElement = document.getDocumentElement();
            System.out.println("根元素: " + rootElement.getNodeName());
            // 4. 获取属性
            String id = rootElement.getAttribute("id");
            System.out.println("学生ID: " + id);
            // 5. 获取子元素
            NodeList nameNodes = rootElement.getElementsByTagName("name");
            if (nameNodes.getLength() > 0) {
                Element nameElement = (Element) nameNodes.item(0);
                System.out.println("学生姓名: " + nameElement.getTextContent());
            }
            NodeList ageNodes = rootElement.getElementsByTagName("age");
            if (ageNodes.getLength() > 0) {
                Element ageElement = (Element) ageNodes.item(0);
                System.out.println("学生年龄: " + ageElement.getTextContent());
            }
        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }
    }
}

SAX (Simple API for XML)

SAX 是一种事件驱动的 XML 解析器,它不会将整个 XML 文档加载到内存中,而是当解析器读取到文档的开始、结束、元素的开始、结束等事件时,会调用相应的方法(回调)。

特点

  • 内存效率高:因为不需要在内存中保存整个文档,所以非常适合处理大型或超大型 XML 文件。
  • 顺序访问:只能从头到尾顺序解析,不能随机访问。
  • 编程复杂:需要实现 ContentHandler 接口,处理各种回调事件。

示例:使用 SAX 解析 XML

创建一个 DefaultHandler 来处理事件

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.ByteArrayInputStream;
public class SaxParserExample {
    public static void main(String[] args) {
        String xml = "<student id=\"1\"><name>张三</name><age>20</age><course>计算机科学</course></student>";
        try {
            // 1. 创建 SAXParserFactory 和 SAXParser
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();
            // 2. 创建自定义的 Handler
            DefaultHandler handler = new DefaultHandler() {
                boolean bName = false;
                boolean bAge = false;
                boolean bCourse = false;
                // 当遇到开始标签时触发
                @Override
                public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                    if (qName.equalsIgnoreCase("name")) {
                        bName = true;
                    }
                    if (qName.equalsIgnoreCase("age")) {
                        bAge = true;
                    }
                    if (qName.equalsIgnoreCase("course")) {
                        bCourse = true;
                    }
                    // 获取属性
                    if (qName.equalsIgnoreCase("student")) {
                        System.out.println("学生ID: " + attributes.getValue("id"));
                    }
                }
                // 当遇到标签内的文本内容时触发
                @Override
                public void characters(char[] ch, int start, int length) throws SAXException {
                    if (bName) {
                        System.out.println("学生姓名: " + new String(ch, start, length));
                        bName = false;
                    }
                    if (bAge) {
                        System.out.println("学生年龄: " + new String(ch, start, length));
                        bAge = false;
                    }
                    if (bCourse) {
                        System.out.println("学生课程: " + new String(ch, start, length));
                        bCourse = false;
                    }
                }
                // 当遇到结束标签时触发
                @Override
                public void endElement(String uri, String localName, String qName) throws SAXException {
                    // System.out.println("结束标签: " + qName);
                }
            };
            // 3. 开始解析
            saxParser.parse(new ByteArrayInputStream(xml.getBytes()), handler);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结与对比

特性 JAXB DOM SAX
核心思想 数据绑定:将 XML 直接映射成 Java 对象。 树模型:将 XML 文档在内存中构建成一棵树。 事件驱动:解析器触发事件,由程序处理。
易用性 非常高,几行代码即可完成序列化和反序列化。 中等,需要理解树结构,节点操作较多。 较低,需要处理各种回调事件,代码量较大。
内存消耗 中等,取决于对象的大小和结构。 ,整个文档都加载到内存中。 非常低,不一次性加载文档,逐行处理。
性能 较快,但不如 SAX。 较慢,尤其是大文件时。 非常高,特别是对于大文件。
适用场景 对象与 XML 的转换,如配置文件、Web Service (SOAP)、数据持久化。 需要频繁修改或随机访问 XML 结构的场景。 解析大型或超大型 XML 文件,或内存受限的环境。
Java 版本 Java 6+ (内置) Java 标准库 Java 标准库

如何选择?

  • 如果你的任务是让一个 Java 对象(或一组对象)持久化为 XML,或者从 XML 文件中恢复一个对象JAXB 是你的不二之选,它最简单、最直接,也最符合面向对象的思想。
  • 如果你的程序需要对一个 XML 文件进行复杂的查询、修改或重组,并且文件不是特别大DOM 是一个不错的选择,因为它提供了灵活的树形操作。
  • 如果你需要处理一个几百MB甚至上GB的日志文件或数据文件,使用 DOM 会导致内存溢出,这时必须使用 SAX 来保证程序的稳定性和低内存占用。
分享:
扫描分享到社交APP
上一篇
下一篇