我正在开发一个系统,该系统应该能够读取任何(或至少是任何格式良好的)XML 文件,操作一些节点并将它们写回到同一个文件中。我希望我的代码尽可能通用,但我不希望
- 对代码中任何位置的架构/文档类型信息的硬编码引用。文档类型信息位于源文档中,我想准确保留该文档类型信息,而不是在我的代码中再次提供它。如果文档没有 DocType,我不会添加。除了我的几个节点之外,我根本不关心这些文件的形式或内容。
- 自定义 EntityResolvers 或 StreamFilters 来省略或以其他方式操纵源信息(遗憾的是,命名空间信息似乎无法从声明它的文档文件中访问,但我可以使用更丑陋的 XPath 进行管理)
- 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]对象,但它不知何故错误,为空或被忽略。我测试的文件是这样开始的(但其他文件类型也是如此):
#37197;置系统“log4j.dtd”>
[...]
我认为有很多(简单?)方法涉及 hack 或将额外的 JAR 引入项目。但我宁愿将它与我已经使用的工具一起使用。