轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

    • 核心

    • 并发

    • 经验

    • JVM

    • 企业应用

      • Freemarker模板实战指南
      • Servlet与JSP指南
      • Java日志系统
      • Java JSON处理
      • Java XML处理
        • 一、XML 基础知识
          • 1. 什么是 XML
          • 2. 为什么 Java 项目中还在用 XML
        • 二、Java XML 处理技术概览
          • 1. DOM 解析:全量加载方式
          • 2. SAX 解析:事件驱动方式
          • 3. StAX 解析:流式处理方式
          • 4. XPath:像 SQL 一样查询 XML
          • 5. XSLT:让 XML 变身的魔法
        • 三、实战:常用 XML 操作技巧
          • 1. 动态创建 XML 文档
          • 2. 从字符串解析 XML
          • 3. SAX 解析实战示例
          • 4. 修改 XML 文档内容
          • 5. 删除指定节点
          • 6. 合并多个 XML 文档
          • 7. 灵活查询 XML 数据
        • 四、主流 XML 处理库和工具
          • 1. JAXP:Java 官方标准
          • 2. JAXB:对象和 XML 的互转神器
          • 2.1 示例
          • 3. JDOM:最直观的 API
          • 4. DOM4J:企业级的选择
          • 5. XStream:一行代码解决对象序列化
          • 6. Jackson XML:万能的数据格式处理器
        • 五、实际开发中的选择建议
          • 1. 按场景选择
          • 2. 性能调优小贴士
          • 3. 安全性注意事项
      • Java对象池技术
      • Java程序的单元测试(Junit)
      • Thymeleaf官方文档(中文版)
      • Mockito应用
      • Java中的空安全
  • Spring

  • 其他语言

  • 工具

  • 后端
  • Java
  • 企业应用
轩辕李
2023-06-11
目录

Java XML处理

在企业应用开发中,XML 作为数据交换和配置管理的重要格式,几乎是每个 Java 开发者都要接触的技术。虽然现在 JSON 很流行,但在很多传统系统、企业级应用和标准协议中,XML 依然占据着不可替代的地位。

本文将带你深入了解 Java 中处理 XML 的各种方式,从基础的解析方法到实际项目中的最佳实践,帮你在面对 XML 处理需求时能够游刃有余。

# 一、XML 基础知识

# 1. 什么是 XML

XML(Extensible Markup Language)是一种标记语言,主要用于数据的存储和传输。它的结构清晰、可读性强,特别适合在不同系统间进行数据交换。

# 2. 为什么 Java 项目中还在用 XML

虽然 JSON 更轻量,但 XML 在以下场景中仍有优势:

  • 企业级应用:很多企业系统的接口标准就是基于 XML
  • 配置文件:Spring、Maven 等框架广泛使用 XML 配置
  • 文档标准:SOAP Web Service、Office 文档格式等
  • 数据验证:XML Schema 提供了强大的数据验证能力

# 二、Java XML 处理技术概览

Java 处理 XML 主要有几种方式,各有特点:

  • DOM:把整个文档加载到内存,适合小文件的增删改查
  • SAX:事件驱动,内存占用少,适合大文件只读处理
  • StAX:流式处理,在性能和易用性间取得平衡
  • JAXB:对象绑定,让你像操作普通 Java 对象一样处理 XML

接下来我们用一个具体的例子来演示这些技术。假设我们有这样一个书店的 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore xmlns="http://www.example.com/bookstore"
        xmlns:author="http://www.example.com/author"
        xmlns:publisher="http://www.example.com/publisher">
    <book category="fiction">
        <title>Harry Potter and the Philosopher's Stone</title>
        <author:author>
            <author:name>J.K. Rowling</author:name>
            <author:birthplace>England</author:birthplace>
        </author:author>
        <publisher:publisher>
            <publisher:name>Bloomsbury</publisher:name>
            <publisher:location>London</publisher:location>
        </publisher:publisher>
        <price currency="USD">15.99</price>
    </book>
    <book category="non-fiction">
        <title>The Lean Startup</title>
        <author:author>
            <author:name>Eric Ries</author:name>
            <author:birthplace>United States</author:birthplace>
        </author:author>
        <publisher:publisher>
            <publisher:name>Random House</publisher:name>
            <publisher:location>New York</publisher:location>
        </publisher:publisher>
        <price currency="USD">19.99</price>
    </book>
</bookstore>

# 1. DOM 解析:全量加载方式

DOM 解析是最直观的方式——就像把一本书全部加载到内存里,然后你可以随意翻阅、修改任意一页。它会把 XML 文档转换成一个树形结构,你可以轻松地增删改查任意节点。

优点:操作灵活,可随意修改文档任意部分
缺点:内存占用大,不适合处理大文件

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;

try {
    // 初始化 DOM 解析器
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true); // 支持命名空间
    DocumentBuilder builder = factory.newDocumentBuilder();
    
    // 解析 XML 文档
    Document document = builder.parse("data.xml");
    
    // 获取所有书籍节点
    NodeList bookList = document.getElementsByTagName("book");
    
    for (int i = 0; i < bookList.getLength(); i++) {
        Element book = (Element) bookList.item(i);
        
        // 读取书籍信息
        String category = book.getAttribute("category");
        String title = book.getElementsByTagName("title").item(0).getTextContent();
        
        // 处理命名空间
        NodeList authorNodes = book.getElementsByTagNameNS(
            "http://www.example.com/author", "author");
        String authorName = "";
        if (authorNodes.getLength() > 0) {
            Element author = (Element) authorNodes.item(0);
            authorName = author.getElementsByTagNameNS(
                "http://www.example.com/author", "name").item(0).getTextContent();
        }
        
        System.out.printf("书籍: %s, 作者: %s, 类别: %s%n", 
                         title, authorName, category);
    }
} catch (Exception e) {
    e.printStackTrace();
}

# 2. SAX 解析:事件驱动方式

SAX 解析就像流水线上的工人——XML 数据一个个元素地流过,你只能在它们经过的时候处理,不能“回头看”。这种方式内存占用少,特别适合处理大型 XML 文件。

优点:内存占用小,适合大文件
缺点:只能顺序读取,不能修改文档

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

try {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.setNamespaceAware(true);
    SAXParser parser = factory.newSAXParser();
    
    // 自定义事件处理器
    DefaultHandler handler = new DefaultHandler() {
        private String currentElement = "";
        private StringBuilder content = new StringBuilder();
        private String currentBook = "";
        
        @Override
        public void startElement(String uri, String localName, String qName, 
                               Attributes attributes) {
            currentElement = localName.isEmpty() ? qName : localName;
            content.setLength(0); // 清空内容
            
            if ("book".equals(currentElement)) {
                String category = attributes.getValue("category");
                System.out.println("开始处理书籍,类别: " + category);
            }
        }
        
        @Override
        public void characters(char[] ch, int start, int length) {
            content.append(ch, start, length);
        }
        
        @Override
        public void endElement(String uri, String localName, String qName) {
            String elementName = localName.isEmpty() ? qName : localName;
            String text = content.toString().trim();
            
            if (!text.isEmpty()) {
                switch (elementName) {
                    case "title":
                        System.out.println("书名: " + text);
                        break;
                    case "name":
                        if (uri.contains("author")) {
                            System.out.println("作者: " + text);
                        } else if (uri.contains("publisher")) {
                            System.out.println("出版社: " + text);
                        }
                        break;
                    case "price":
                        System.out.println("价格: " + text);
                        break;
                }
            }
            
            if ("book".equals(elementName)) {
                System.out.println("------");
            }
        }
    };
    
    parser.parse("data.xml", handler);
} catch (Exception e) {
    e.printStackTrace();
}

# 3. StAX 解析:流式处理方式

StAX 是“最好的两个世界”——既有 SAX 的低内存占用,又有 DOM 那样的灵活性。与 SAX 被动接受事件不同,StAX 让你主动“拉取”数据,想读下一个元素的时候再读。

优点:低内存占用 + 灵活控制
缺点:只能向前读取,不能修改文档

import javax.xml.stream.*;
import java.io.FileInputStream;

try {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    XMLStreamReader reader = factory.createXMLStreamReader(
        new FileInputStream("data.xml"));
    
    String currentElement = "";
    while (reader.hasNext()) {
        int event = reader.next();
        
        switch (event) {
            case XMLStreamConstants.START_ELEMENT:
                currentElement = reader.getLocalName();
                
                // 处理属性
                if ("book".equals(currentElement)) {
                    String category = reader.getAttributeValue(null, "category");
                    System.out.println("书籍类别: " + category);
                }
                break;
                
            case XMLStreamConstants.CHARACTERS:
                String text = reader.getText().trim();
                if (!text.isEmpty() && !currentElement.isEmpty()) {
                    switch (currentElement) {
                        case "title":
                            System.out.println("书名: " + text);
                            break;
                        case "name":
                            System.out.println("名称: " + text);
                            break;
                        case "price":
                            System.out.println("价格: " + text);
                            break;
                    }
                }
                break;
                
            case XMLStreamConstants.END_ELEMENT:
                if ("book".equals(reader.getLocalName())) {
                    System.out.println("------");
                }
                break;
        }
    }
    
    reader.close();
} catch (Exception e) {
    e.printStackTrace();
}

# 4. XPath:像 SQL 一样查询 XML

XPath 就像给 XML 设计的 SQL——你可以用一句话就准确地找到你要的数据。比如 /bookstore/book[1]/title 就能直接拿到第一本书的标题。

优点:表达式简洁,查询精准
缺点:需要先用 DOM 加载文档,内存占用较大

import javax.xml.parsers.*;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import javax.xml.namespace.NamespaceContext;
import java.util.Iterator;

try {
    // 先用 DOM 加载文档
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true);
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document document = builder.parse("data.xml");
    
    // 初始化 XPath
    XPathFactory xPathFactory = XPathFactory.newInstance();
    XPath xpath = xPathFactory.newXPath();
    
    // 设置命名空间上下文
    xpath.setNamespaceContext(new NamespaceContext() {
        public String getNamespaceURI(String prefix) {
            switch (prefix) {
                case "bs": return "http://www.example.com/bookstore";
                case "author": return "http://www.example.com/author";
                case "pub": return "http://www.example.com/publisher";
                default: return "";
            }
        }
        public String getPrefix(String uri) { return null; }
        public Iterator<String> getPrefixes(String uri) { return null; }
    });
    
    // 各种实用的 XPath 查询示例
    
    // 1. 获取所有书的标题
    NodeList titles = (NodeList) xpath.evaluate("//bs:book/bs:title", 
                                                document, XPathConstants.NODESET);
    System.out.println("所有书的标题:");
    for (int i = 0; i < titles.getLength(); i++) {
        System.out.println("- " + titles.item(i).getTextContent());
    }
    
    // 2. 查找价格大于 16 的书
    NodeList expensiveBooks = (NodeList) xpath.evaluate(
        "//bs:book[bs:price > 16]/bs:title", document, XPathConstants.NODESET);
    System.out.println("\n价格大于16的书:");
    for (int i = 0; i < expensiveBooks.getLength(); i++) {
        System.out.println("- " + expensiveBooks.item(i).getTextContent());
    }
    
    // 3. 获取第一本书的作者
    String firstAuthor = (String) xpath.evaluate(
        "//bs:book[1]/author:author/author:name", document, XPathConstants.STRING);
    System.out.println("\n第一本书的作者: " + firstAuthor);
    
    // 4. 统计书的数量
    Double bookCount = (Double) xpath.evaluate("count(//bs:book)", 
                                              document, XPathConstants.NUMBER);
    System.out.println("\n书的数量: " + bookCount.intValue());
    
} catch (Exception e) {
    e.printStackTrace();
}

# 5. XSLT:让 XML 变身的魔法

XSLT 就像一个格式转换器,可以把 XML 数据“变身”成 HTML、PDF 或者其他格式。你只需要写一个样式表,告诉它怎么转换,就能把枯燥的 XML 数据转成漂亮的网页。

适用场景:报表生成、数据展示、格式转换

import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

File xmlFile = new File("data.xml");
File xsltFile = new File("transform.xsl");
File outFile = new File("output.html");

StreamSource xmlSource = new StreamSource(xmlFile);
StreamSource xsltSource = new StreamSource(xsltFile);
StreamResult outputResult = new StreamResult(outFile);

TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltSource);

transformer.transform(xmlSource, outputResult);

System.out.println("Transformation completed.");

transform.xsl文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:bookstore="http://www.example.com/bookstore"
        xmlns:author="http://www.example.com/author"
        xmlns:publisher="http://www.example.com/publisher"
        exclude-result-prefixes="bookstore author publisher">

    <xsl:output method="html"/>

    <xsl:template match="/">
        <html>
            <body>
                <h2>Books</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th>Title</th>
                        <th>Author</th>
                        <th>Price</th>
                    </tr>
                    <xsl:apply-templates select="//bookstore:book"/>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="bookstore:book">
        <tr>
            <td><xsl:value-of select="bookstore:title"/></td>
            <td><xsl:value-of select="author:author/author:name"/></td>
            <td><xsl:value-of select="bookstore:price"/></td>
        </tr>
    </xsl:template>
</xsl:stylesheet>

输出的output.html文件内容如下:

<html>
    <body>
        <h2>Books</h2>
        <table border="1">
            <tr bgcolor="#9acd32">
                <th>Title</th><th>Author</th><th>Price</th>
            </tr>
            <tr>
                <td>Harry Potter and the Philosopher's Stone</td><td>J.K. Rowling</td><td>15.99</td>
            </tr>
            <tr>
                <td>The Lean Startup</td><td>Eric Ries</td><td>19.99</td>
            </tr>
        </table>
    </body>
</html>

# 三、实战:常用 XML 操作技巧

现在我们从理论转向实践,看看在实际工作中怎么高效地处理 XML。这些例子都是经过实际项目验证的,可以直接拿来用。

# 1. 动态创建 XML 文档

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;

@Test
public void createXMLDocument() {
    try {
        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 创建文档对象
        Document document = builder.newDocument();

        // 创建根元素
        String rootElementName = "books";
        org.w3c.dom.Element rootElement = document.createElement(rootElementName);
        document.appendChild(rootElement);

        // 创建子元素
        String bookElementName = "book";
        org.w3c.dom.Element bookElement = document.createElement(bookElementName);
        rootElement.appendChild(bookElement);

        // 添加子元素的属性
        bookElement.setAttribute("id", "1");

        // 添加子元素的文本内容
        String title = "Java Programming";
        org.w3c.dom.Element titleElement = document.createElement("title");
        titleElement.appendChild(document.createTextNode(title));
        bookElement.appendChild(titleElement);

        // 将文档转换为字符串
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(document), new StreamResult(writer));
        String xmlString = writer.toString();

        // 断言输出的XML字符串是否符合预期
        String expectedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";
        Assertions.assertEquals(xmlString, expectedXmlString);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 2. 从字符串解析 XML

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

@Test
public void readXMLDocument() {
    try {
        // XML文档字符串
        String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";

        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 从XML字符串创建文档对象
        Document document = builder.parse(new InputSource(new StringReader(xmlString)));

        // 读取XML节点的值
        String title = document.getElementsByTagName("title").item(0).getTextContent();

        // 断言读取的节点值是否符合预期
        Assertions.assertEquals(title, "Java Programming");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 3. SAX 解析实战示例

@Test
public void testSAXParsing() {
    try {
        // 创建 SAXParserFactory 对象
        SAXParserFactory factory = SAXParserFactory.newInstance();

        // 创建 SAXParser 对象
        SAXParser saxParser = factory.newSAXParser();

        // 解析 XML 文档
        String xmlString = "<root><element1>Value 1</element1><element2>Value 2</element2></root>";
        saxParser.parse(new InputSource(new StringReader(xmlString)), new DefaultHandler() {
            @Override
            public void startElement(String uri, String localName, String qName, Attributes attributes)
                    throws SAXException {
                System.out.println("Start Element :" + qName + " " + localName + " " + uri);
            }

            @Override
            public void endElement(String uri, String localName, String qName) throws SAXException {
                System.out.println("End Element :" + qName + " " + localName + " " + uri);
            }

            @Override
            public void characters(char[] ch, int start, int length) throws SAXException {
                String content = new String(ch, start, length).trim();
                if (!content.isEmpty()) {
                    System.out.println("Content :" + content);
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 4. 修改 XML 文档内容

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;

@Test
public void modifyXMLDocument() {
    try {
        // XML文档字符串
        String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";

        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 从XML字符串创建文档对象
        Document document = builder.parse(new InputSource(new StringReader(xmlString)));

        // 修改节点的值
        Element titleElement = (Element) document.getElementsByTagName("title").item(0);
        titleElement.setTextContent("Java Programming 2nd Edition");

        // 将修改后的文档转换为字符串
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(document), new StreamResult(writer));
        String modifiedXmlString = writer.toString();

        // 断言修改后的XML字符串是否符合预期
        String expectedModifiedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming 2nd Edition</title></book></books>";
        Assertions.assertEquals(modifiedXmlString, expectedModifiedXmlString);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 5. 删除指定节点

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;


@Test
public void deleteXMLNode() {
    try {
        // XML文档字符串
        String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";

        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 从XML字符串创建文档对象
        Document document = builder.parse(new InputSource(new StringReader(xmlString)));

        // 获取要删除的节点
        Element bookElement = (Element) document.getElementsByTagName("book").item(0);

        // 删除节点
        Node parentNode = bookElement.getParentNode();
        parentNode.removeChild(bookElement);

        // 将修改后的文档转换为字符串
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(document), new StreamResult(writer));
        String modifiedXmlString = writer.toString();

        // 断言删除节点后的XML字符串是否符合预期
        String expectedModifiedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books/>";
        Assertions.assertEquals(modifiedXmlString, expectedModifiedXmlString);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 6. 合并多个 XML 文档

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;

@Test
public void mergeXMLDocuments() {
    try {
        // XML文档字符串1
        String xmlString1 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book></books>";

        // XML文档字符串2
        String xmlString2 = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"2\"><title>Python Programming</title></book></books>";

        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 从XML字符串创建文档对象1
        Document document1 = builder.parse(new InputSource(new StringReader(xmlString1)));

        // 从XML字符串创建文档对象2
        Document document2 = builder.parse(new InputSource(new StringReader(xmlString2)));

        // 获取根节点
        Element rootElement1 = document1.getDocumentElement();
        Element rootElement2 = document2.getDocumentElement();

        // 将文档2中的所有子节点添加到文档1的根节点下
        NodeList childNodes = rootElement2.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            Node node = childNodes.item(i);
            Node importedNode = document1.importNode(node, true);
            rootElement1.appendChild(importedNode);
        }

        // 将合并后的文档转换为字符串
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(document1), new StreamResult(writer));
        String mergedXmlString = writer.toString();

        // 断言合并后的XML字符串是否符合预期
        String expectedMergedXmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book><book id=\"2\"><title>Python Programming</title></book></books>";
        Assertions.assertEquals(mergedXmlString, expectedMergedXmlString);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 7. 灵活查询 XML 数据

import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;


@Test
public void queryXMLData() {
    try {
        // XML文档字符串
        String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><books><book id=\"1\"><title>Java Programming</title></book><book id=\"2\"><title>Python Programming</title></book></books>";

        // 创建文档工厂
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        // 从XML字符串创建文档对象
        Document document = builder.parse(new InputSource(new StringReader(xmlString)));

        // 查询XML数据
        NodeList bookList = document.getElementsByTagName("book");
        for (int i = 0; i < bookList.getLength(); i++) {
            Node bookNode = bookList.item(i);
            if (bookNode.getNodeType() == Node.ELEMENT_NODE) {
                Element bookElement = (Element) bookNode;
                String bookId = bookElement.getAttribute("id");
                String title = bookElement.getElementsByTagName("title").item(0).getTextContent();
                Assertions.assertEquals(bookId, i == 0 ? "1" : "2");
                Assertions.assertEquals(title, i == 0 ? "Java Programming" : "Python Programming");
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# 四、主流 XML 处理库和工具

除了 Java 原生 API,还有一些优秀的第三方库可以让 XML 处理更加轻松。根据我的经验,这里推荐几个在实际项目中经常用到的。

# 1. JAXP:Java 官方标准

JAXP 是 Java 内置的 XML 处理标准,就像是 XML 处理的“官方配套”。不管你用哪种 JDK,都能直接使用,不需要额外的依赖。

优点:

  • 官方支持,稳定可靠
  • 支持 DOM、SAX、StAX 等多种解析方式
  • 跨平台,兼容性好

缺点:

  • API 相对复杂,上手成本高
  • 功能相对基础,高级特性支持不够

# 2. JAXB:对象和 XML 的互转神器

JAXB (opens new window) 可能是最“懒人”的 XML 处理方式了——你只需要在 Java 类上加几个注解,就能让对象和 XML 之间自动转换。就像有了一个自动翻译器,不用你手动解析 XML。

优点:

  • 代码量少,开发效率高
  • 类型安全,编译时可检查错误
  • 支持复杂的对象关系映射

缺点:

  • 需要额外的库依赖
  • 对复杂 XML 结构支持不够灵活

# 2.1 示例

下面是一个使用 JAXB 进行 XML 到 Java 对象和 Java 对象到 XML 的示例:

首先,定义一个 Java 类 Book,用于表示图书信息:

import jakarta.xml.bind.annotation.*;

@XmlRootElement(name = "book")
@XmlAccessorType(XmlAccessType.FIELD)
public class Book {

    @XmlAttribute
    private int id;

    @XmlElement
    private String title;

    // Getter and Setter methods
    // ...
}

然后,可以使用 JAXB 将 XML 转换为 Java 对象(XML 到 Java 对象):

import jakarta.xml.bind.*;
import java.io.File;

public class XMLToBeanExample {

    public static void main(String[] args) {
        try {
            // 创建 JAXBContext 实例
            JAXBContext context = JAXBContext.newInstance(Book.class);

            // 创建 Unmarshaller 实例
            Unmarshaller unmarshaller = context.createUnmarshaller();

            // 从 XML 文件中读取 Book 对象
            File xmlFile = new File("book.xml");
            Book book = (Book) unmarshaller.unmarshal(xmlFile);

            // 输出 Java 对象内容
            System.out.println("Book ID: " + book.getId());
            System.out.println("Title: " + book.getTitle());
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

上述代码将从名为 "book.xml" 的 XML 文件中读取 Book 对象,并打印出其中的属性值。

接下来,可以使用 JAXB 将 Java 对象转换为 XML(Java 对象到 XML):

import jakarta.xml.bind.*;
import java.io.File;

public class BeanToXMLExample {

    public static void main(String[] args) {
        try {
            // 创建 JAXBContext 实例
            JAXBContext context = JAXBContext.newInstance(Book.class);

            // 创建 Marshaller 实例
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            // 创建 Book 对象
            Book book = new Book();
            book.setId(1);
            book.setTitle("Java Programming");

            // 将 Book 对象转换为 XML 文件
            File xmlFile = new File("book.xml");
            marshaller.marshal(book, xmlFile);

            System.out.println("XML file created successfully.");
        } catch (JAXBException e) {
            e.printStackTrace();
        }
    }
}

上述代码将创建一个 Book 对象,并将其转换为 XML 格式后写入名为 "book.xml" 的文件中。

这是一个简单的使用 JAXB 进行 XML 和 Java 对象之间转换的示例。通过定义适当的注解或配置文件,可以更复杂地映射 XML 结构和 Java 类的关系,以满足更具体的需求。

# 3. JDOM:最直观的 API

JDOM (opens new window) 就像是给 XML 处理重新设计了 API,让你操作 XML 就像操作普通的 Java 对象一样简单。不用再为复杂的 DOM API 头疼了。

优点:

  • API 设计直观,学习成本低
  • 代码可读性强,维护方便
  • 支持的功能全面

缺点:

  • 性能相对一般
  • 社区活跃度不高

参考: Getting Started (opens new window)

# 4. DOM4J:企业级的选择

DOM4J (opens new window) 可谓是 XML 处理界的“瓦尔特”——功能强大、性能优秀,在很多企业级项目中都有它的身影。特别是处理复杂、大型 XML 文档时,性能表现优秀。

优点:

  • 性能优秀,特别适合大型 XML
  • 支持强大的 XPath 查询
  • API 设计合理,功能全面
  • 在企业级项目中应用广泛

缺点:

  • 需要额外的依赖
  • 学习成本相对较高

参考: Quick Start (opens new window)

# 5. XStream:一行代码解决对象序列化

XStream (opens new window) 是最“懒”的对象序列化工具——一行代码就能把任意 Java 对象转成 XML,再一行代码转回来。不需要任何注解,不需要配置文件。

优点:

  • 使用极其简单,几乎零配置
  • 对各种复杂对象支持好
  • 生成的 XML 格式简洁

缺点:

  • 生成的 XML 结构定制化程度低
  • 安全性需要特别注意

参考: Tutorial (opens new window)

# 6. Jackson XML:万能的数据格式处理器

在Java JSON处理中我们介绍过 Jackson 的强大功能。现在它的 XML 模块 (opens new window)也能给你带来同样的便利性。

如果你的项目里已经在用 Jackson 处理 JSON,那么增加 XML 支持就是加一个依赖的事情。API 风格完全一致,学习成本几乎为零。

优点:

  • 与 Jackson JSON 处理风格一致
  • 性能优秀,特别是大数据量处理
  • 注解支持丰富,定制化程度高

缺点:

  • 对一些复杂 XML 特性支持不全
  • 学习成本相对较高

# 五、实际开发中的选择建议

读到这里,你可能会问:这么多种方式,在实际项目中到底该选哪一种?根据我多年的开发经验,给你几个建议:

# 1. 按场景选择

小文件,需要修改:直接用 DOM,简单粗暴 大文件,只读取:首选 SAX,内存占用小 需要精准查询:用 XPath,一句话搞定 对象与 XML 互转:优先 JAXB,实在不行考虑 XStream 企业级项目:DOM4J 是首选,成熟稳定

# 2. 性能调优小贴士

  1. 合理设置缓冲区:处理大文件时,调整 BufferedInputStream 的缓冲区大小
  2. 关闭不必要的验证:如果信任 XML 源,可以关闭 DTD 验证提高性能
  3. 复用解析器实例:DocumentBuilder 等对象创建成本较高,能复用就复用
  4. 及时释放资源:特别是使用 StAX 时,记得关闭 reader

# 3. 安全性注意事项

  • XXE 攻击防护:在生产环境中处理外部 XML 时,一定要禁用外部实体解析
  • 内存溢出防护:对输入 XML 的大小做限制,防止恶意 XML 攻击
  • 输入校验:对于用户输入的 XML,做好格式和内容校验

总的来说,XML 处理虽然看起来复杂,但掌握了这些技术后,你就能在面对各种 XML 处理需求时得心应手。记住,没有最好的技术,只有最适合的选择。

祝你变得更强!

编辑 (opens new window)
#XML
上次更新: 2025/08/16
Java JSON处理
Java对象池技术

← Java JSON处理 Java对象池技术→

最近更新
01
AI时代的编程心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code实战之供应商切换工具
08-18
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式