You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中合并XML文件:按元素类型分组并保留顺序

按元素类型分组合并XML(适配OOXML编号规则)

解决方案思路

要实现按元素类型分组、保留同类型元素原有顺序的XML合并,核心是先按标签名对两个文档的子元素分组存储,再按指定顺序将各组元素依次拼接:

  1. 分别解析两个XML文档,提取根节点(parent)下的所有子元素
  2. 用有序Map按标签名分组存储元素(比如A组、B组),先存入第一个文档的元素,再将第二个文档的对应标签元素追加到同组列表
  3. 按原文档的标签顺序(如先A后B),将所有分组元素依次添加到新的parent节点中
  4. 生成符合格式要求的XML输出

Java 实现代码

以下是基于原生DOM API的实现示例,适配你提供的XML结构:

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
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.ByteArrayInputStream;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

public class XmlMerger {
    public static void main(String[] args) throws Exception {
        // 两个输入XML字符串
        String xml1 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<parent>\n  <A id=\"1\">...</A>\n  <A id=\"2\">...</A>\n  <B id=\"1\">...</B>\n  <B id=\"2\">...</B>\n</parent>";
        String xml2 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<parent>\n  <A id=\"3\">...</A>\n  <B id=\"3\">...</B>\n</parent>";

        // 解析两个XML,获取子元素列表
        List<Node> nodes1 = getChildNodesFromXml(xml1);
        List<Node> nodes2 = getChildNodesFromXml(xml2);

        // 按标签名分组,LinkedHashMap保证标签顺序和原文档一致
        Map<String, List<Node>> groupMap = new LinkedHashMap<>();

        // 加入第一个XML的元素
        for (Node node : nodes1) {
            String tagName = node.getNodeName();
            groupMap.computeIfAbsent(tagName, k -> new ArrayList<>()).add(node);
        }

        // 加入第二个XML的元素(追加到对应分组)
        Document newDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
        for (Node node : nodes2) {
            String tagName = node.getNodeName();
            // 导入节点到新文档,避免跨文档节点异常
            Node importedNode = newDoc.importNode(node, true);
            groupMap.computeIfAbsent(tagName, k -> new ArrayList<>()).add(importedNode);
        }

        // 构建新的XML文档
        Node newParent = newDoc.createElement("parent");
        newDoc.appendChild(newParent);

        // 按分组顺序添加元素
        for (List<Node> nodes : groupMap.values()) {
            for (Node node : nodes) {
                // 如果是从第一个文档来的节点,需要先导入
                if (!newDoc.equals(node.getOwnerDocument())) {
                    node = newDoc.importNode(node, true);
                }
                newParent.appendChild(node);
            }
        }

        // 输出格式化后的XML
        System.out.println(formatXml(newDoc));
    }

    // 解析XML字符串,获取parent下的子元素列表
    private static List<Node> getChildNodesFromXml(String xml) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
        Node parent = doc.getDocumentElement();
        NodeList nodeList = parent.getChildNodes();
        List<Node> nodes = new ArrayList<>();
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node node = nodeList.item(i);
            // 只保留元素节点(跳过空白文本节点)
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                nodes.add(node);
            }
        }
        return nodes;
    }

    // 格式化XML输出
    private static String formatXml(Document doc) throws Exception {
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        // 设置缩进和编码
        transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(javax.xml.transform.OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        StringWriter writer = new StringWriter();
        transformer.transform(new DOMSource(doc), new StreamResult(writer));
        return writer.toString();
    }
}

关键细节说明

  1. 有序Map的使用:用LinkedHashMap存储分组,确保标签顺序和第一个文档中的出现顺序一致(比如原文档先A后B,合并后依然保持这个顺序)
  2. 节点导入:跨文档的节点不能直接添加,必须用importNode方法导入到新文档中,避免DOM异常
  3. 过滤空白节点:解析时跳过空白文本节点,避免合并后出现多余的空行或空格
  4. OOXML适配:对于Word的numbering.xml,标签顺序(如<abstractNum>在前,<num>在后)是固定的,此方法会严格保留原文档的标签顺序,符合OOXML的规范要求

内容的提问来源于stack exchange,提问作者Kevin Cruijssen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 10:50:25