Java中合并XML文件:按元素类型分组并保留顺序
按元素类型分组合并XML(适配OOXML编号规则)
解决方案思路
要实现按元素类型分组、保留同类型元素原有顺序的XML合并,核心是先按标签名对两个文档的子元素分组存储,再按指定顺序将各组元素依次拼接:
- 分别解析两个XML文档,提取根节点(
parent)下的所有子元素 - 用有序Map按标签名分组存储元素(比如
A组、B组),先存入第一个文档的元素,再将第二个文档的对应标签元素追加到同组列表 - 按原文档的标签顺序(如先
A后B),将所有分组元素依次添加到新的parent节点中 - 生成符合格式要求的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(); } }
关键细节说明
- 有序Map的使用:用
LinkedHashMap存储分组,确保标签顺序和第一个文档中的出现顺序一致(比如原文档先A后B,合并后依然保持这个顺序) - 节点导入:跨文档的节点不能直接添加,必须用
importNode方法导入到新文档中,避免DOM异常 - 过滤空白节点:解析时跳过空白文本节点,避免合并后出现多余的空行或空格
- OOXML适配:对于Word的
numbering.xml,标签顺序(如<abstractNum>在前,<num>在后)是固定的,此方法会严格保留原文档的标签顺序,符合OOXML的规范要求
内容的提问来源于stack exchange,提问作者Kevin Cruijssen
相关产品推荐
相关产品推荐

