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

Transformer为何改变XML节点属性顺序?求保留原结构的Java方案

问题描述

我们使用Java的TransformerFactory和Transformer删除XML中的特定节点后保存文件,发现生成的新XML里属性被自动按字母顺序排序了。需要保留XML原有结构(包括属性的原始顺序),避免不必要的修改。

输入XML示例

<employee EndDate="12/31/2025" empCode="8881" changeType="change" employeeNum="000" BeginDate="08/01/2014" employeeName="Jose Milton" departmentPrefix="ISEC" stateId="93">

原有处理代码

if (BeginDate.equals(EndDate)) { 
    element.getParentNode().removeChild(element);
}

String outPath = outFolder.getAbsolutePath() + File.separator + inputXml.getName();
String outFileName = outPath.replace(outPath.substring(126, 129), "555");

TransformerFactory transformerFactory = TransformerFactory.newInstance(); 
Transformer transformer = transformerFactory.newTransformer(); 
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(outFileName)); 
transformer.transform(source, result);

问题输出(属性被排序)

<employee BeginDate="08/01/2014" changeType="change" departmentPrefix="ISEC" empCode="8881" employeeName="Jose Milton" employeeNum="800" EndDate="12/31/2025" stateId="93">
解决方案

默认的Transformer实现会对XML属性按字母顺序排序,要保留原始属性顺序,推荐使用**DOM Level 3的LSSerializer**来序列化XML,它可以直接控制属性的输出顺序,只要解析XML时保留了属性顺序(大部分现代DOM解析器默认会保留)。

修改后的代码(替换原Transformer序列化部分)

if (BeginDate.equals(EndDate)) { 
    element.getParentNode().removeChild(element);
}

String outPath = outFolder.getAbsolutePath() + File.separator + inputXml.getName();
String outFileName = outPath.replace(outPath.substring(126, 129), "555");

// 使用LSSerializer序列化DOM,保留属性顺序
DOMImplementation domImpl = document.getImplementation();
DOMImplementationLS domImplLS = (DOMImplementationLS) domImpl.getFeature("LS", "3.0");
LSSerializer serializer = domImplLS.createLSSerializer();

// 配置序列化参数:指定编码、输出流
LSOutput output = domImplLS.createLSOutput();
output.setEncoding("UTF-8");
output.setByteStream(new FileOutputStream(new File(outFileName)));

// 执行序列化
serializer.write(document, output);

关键说明

  • LSSerializer是DOM标准的一部分,JDK 1.6及以上自带,无需额外引入依赖。
  • 解析XML时保持默认配置即可保留属性顺序,示例解析代码:
    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    factory.setNamespaceAware(true); // 可选,避免命名空间解析异常
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document document = builder.parse(inputXml);
    
  • 如果需要格式化输出(换行、缩进),可添加以下配置:
    serializer.getDomConfig().setParameter("format-pretty-print", true);
    
备选方案(依赖特定解析器)

如果必须使用Transformer,可尝试设置TransformerFactory属性强制关闭属性排序,但该方法依赖具体解析器(如Apache Xerces),通用性较差:

TransformerFactory transformerFactory = TransformerFactory.newInstance();
// 针对Xerces设置不排序属性
transformerFactory.setAttribute("http://apache.org/xml/properties/serializer/sort-attributes", Boolean.FALSE);
Transformer transformer = transformerFactory.newTransformer();
// 后续序列化代码不变

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:08:37