Java 在编写XML文件时,如何忽略DTD验证而保留Doctype?
我正在开发一个系统,该系统应该能够读取任何(或至少是任何格式良好的)XML文件,操作一些节点并将它们写回同一个文件。我希望我的代码尽可能通用,但我不希望Java 在编写XML文件时,如何忽略DTD验证而保留Doctype?,java,xml,dtd,doctype,Java,Xml,Dtd,Doctype,我正在开发一个系统,该系统应该能够读取任何(或至少是任何格式良好的)XML文件,操作一些节点并将它们写回同一个文件。我希望我的代码尽可能通用,但我不希望 在我的代码中任何位置对架构/Doctype信息的硬编码引用。doctype信息在源文档中,我希望准确地保留该doctype信息,而不是从代码中再次提供它。如果文档没有DocType,我不会添加DocType。除了我的几个节点之外,我根本不关心这些文件的形式或内容 自定义EntityResolver或StreamFilter以省略或以其他方式操
- 在我的代码中任何位置对架构/Doctype信息的硬编码引用。doctype信息在源文档中,我希望准确地保留该doctype信息,而不是从代码中再次提供它。如果文档没有DocType,我不会添加DocType。除了我的几个节点之外,我根本不关心这些文件的形式或内容
- 自定义EntityResolver或StreamFilter以省略或以其他方式操作源信息(很遗憾,命名空间信息似乎无法从声明它的文档文件中访问,但我可以使用更丑陋的XPath进行管理)
- DTD验证。我没有引用的DTD,我不想包含它们,在不知道它们的情况下,节点操作是完全可能的李>
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标签不见了,不管我做什么。在调试过程中,我看到解析后的文档对象中有一个DeferredDoctypeImpl[log4j:configuration:null]对象,但不知何故它是错误的、空的或被忽略的。我测试的文件开始时是这样的(但其他文件类型也是这样):
[……]
我认为有很多(简单的?)方法涉及到黑客或者在项目中添加额外的jar。但我更愿意用我已经使用过的工具来使用它 很抱歉,现在使用的是XMLSerializer而不是Transformer…下面是如何使用JDK中的LSSerializer实现的:
private void writeDocument(Document doc, String filename)
throws IOException {
Writer writer = null;
try {
/*
* Could extract "ls" to an instance attribute, so it can be reused.
*/
DOMImplementationLS ls = (DOMImplementationLS)
DOMImplementationRegistry.newInstance().
getDOMImplementation("LS");
writer = new OutputStreamWriter(new FileOutputStream(filename));
LSOutput lsout = ls.createLSOutput();
lsout.setCharacterStream(writer);
/*
* If "doc" has been constructed by parsing an XML document, we
* should keep its encoding when serializing it; if it has been
* constructed in memory, its encoding has to be decided by the
* client code.
*/
lsout.setEncoding(doc.getXmlEncoding());
LSSerializer serializer = ls.createLSSerializer();
serializer.write(doc, lsout);
} catch (Exception e) {
throw new IOException(e);
} finally {
if (writer != null) writer.close();
}
}
所需进口:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;
我知道这是一个已经被回答过的老问题,但我认为技术细节可能会对某些人有所帮助。我尝试使用LSSerializer库,但在保留Doctype方面却没有取得任何进展。这就是Stephan可能使用的解决方案 注意:这在scala中,但使用java库,所以只需转换代码即可
我想看看你的代码。尽管如此,我还是会利用谷歌的强大力量@Stephan(此处没有登录者帐户的用户)
import com.sun.org.apache.xml.internal.serialize.{OutputFormat, XMLSerializer}
def transformXML(root: Element, file: String): Unit = {
val doc = root.getOwnerDocument
val format = new OutputFormat(doc)
format.setIndenting(true)
val writer = new OutputStreamWriter(new FileOutputStream(new File(file)))
val serializer = new XMLSerializer(writer, format)
serializer.serialize(doc)
}