我正在开发一个系统,该系统应该能够读取任何(或至少任何格式良好的)XML 文件,操作一些节点并将它们写回到同一个文件中。我希望我的代码尽可能通用,我不希望
- 在我的代码中的任何位置对 Schema/Doctype 信息的硬编码引用。doctype 信息在源文档中,我想准确地保留该 doctype 信息,而不是在我的代码中再次提供它。如果一个文档没有 DocType,我不会添加一个。我根本不关心这些文件的形式或内容,除了我的几个节点。
- 自定义 EntityResolvers 或 StreamFilters 以省略或以其他方式操作源信息(很遗憾,命名空间信息似乎无法从声明它的文档文件中访问,但我可以通过使用更丑的 XPaths 来管理)
- DTD 验证。我没有引用的 DTD,我不想包含它们,并且在不知道它们的情况下完全可以进行节点操作。
目的是使源文件完全不变,除了通过 XPath 检索的已更改节点。我想摆脱标准的 javax.xml 东西。
到目前为止我的进展:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setAttribute("http://xml.org/sax/features/namespaces", true);
factory.setAttribute("http://xml.org/sax/features/validation", false);
factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
factory.setNamespaceAware(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setIgnoringComments(false);
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(inStream));
这会成功地将 XML 源加载到 org.w3c.dom.Document 中,而忽略 DTD 验证。我可以做我的替换,然后我使用
Source source = new DOMSource(document);
Result result = new StreamResult(getOutputStream(getPath()));
// Write the DOM document to the file
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);
把它写回来。这几乎是完美的。但是无论我做什么,Doctype 标签都消失了。在调试的时候,看到解析后的Document对象中有一个DeferredDoctypeImpl [log4j:configuration: null]对象,但是不知怎么的,它是错误的,为空或被忽略。我测试的文件是这样开始的(但其他文件类型也是如此):
<?xml 版本="1.0" 编码="UTF-8"?>
<!DOCTYPE log4j:配置系统“log4j.dtd”>
<log4j:配置 xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
[...]
我认为有很多(简单的?)方法涉及黑客或将额外的 JAR 拉入项目。但我更愿意将它与我已经使用的工具一起使用。