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
相关产品推荐
相关产品推荐

