我想将多个XML节点从源XML文件复制到目标文件。源文件和目标文件都非常大,所以我将使用StAX。通常,我正在尝试处理的文件如下所示:
<root>
<header>
<title>A List of persons</title>
</header>
<person>
<name>Joe</name>
<surname>Bloggs</surname>
</person>
<person>
<name>John</name>
<surname>Doe</surname>
</person>
.
.
etc...
</root>
目标文件应采用以下格式:
<root>
<header>
<title>A List of persons</title>
</header>
<person>
<name>Joe</name>
<surname>Bloggs</surname>
</person>
</root>
其中每个文件应包含标头节点,只有一个 person 节点全部包含在 root 节点中。
现在我的问题如下:我正在尝试通过XMLStreamReader读取源文件,并使用XMLStreamWriter编写它们,两者都连接到一个复制片段的Transformer实例从源文件到目标文件。变换器创建如下:
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
StAXSource stAXSource = new StAXSource(reader);
StAXResult stAXResult = new StAXResult(writer);
我还有一个自定义方法,它将光标移动到XML输入流中的所需片段:
// Moves XMLStreamReader cursor to the next fragment.
moveCursorToNextFragment(XMLStreamReader reader, String fragmentNodeName)
所以最后我得到了以下结论:
// Open file as usual...
// Advance cursor to <header> node, and copy fragment till
// </header> to the output writer.
moveCursorToNextFragment(reader, "header");
transformer.transform(stAXSource, stAXResult);
// Advance cursor to <person> node, and copy fragment till
// </person> to the output writer.
moveCursorToNextFragment(reader, "person");
transformer.transform(stAXSource, stAXResult);
问题是生成的XML文件包含2个XML声明部分,每次调用一个
transformer.transform(stAXSource, stAXResult);
我尝试使用 StreamResult 转换输出,如下所示:
transformer.transform(stAXSource, new StreamResult(myStream));
并省略了XML声明,但当我恢复使用 StAXResult 时,XML声明又回来了。我还注意到OutputKeys.OMIT_XML_DECLARATION无论是打开还是关闭都没有效果(如其他设置,如OutputKeys.STANDALONE,值为“是”)。
简而言之,当StAXResult作为目标结果时,似乎忽略了在Transformer上全局设置的这些设置。
我的问题是:有没有办法可以实现这一点,以便Transformer在每次调用Transformer.transform()时都不会发出XML声明(即写入没有XML声明的片段)?
非常感谢您的帮助。
答案 0 :(得分:0)
Xalan的SAX2StAXStreamWriter
正在这样做。另一个XSLT实现可能表现不同。为了解决这个问题,您可以包装您的编写器并强制startDocument(...)
方法不执行任何操作。 StAXON库提供了StreamWriterDelegate
实用程序类,它有助于保持必要的代码简短:
writer = new StreamWriterDelegate(writer) {
@Override public void writeStartDocument() {}
@Override public void writeStartDocument(String version) {}
@Override public void writeStartDocument(String encoding, String version) {}
};
应该这样做。
答案 1 :(得分:0)
基于@chris 的回答,我实现了一个不依赖于 StAXON 的版本。我使用 Zulu、OpenJDK、Java 11 对此进行了测试。
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* Implementation of XMLStreamWriter that will not write the XML tags
* allowing us to use a transformer to write DOM objects using STAX
* but avoid having <?xml version="1.0" ?> tags generated for each
* invocation of the transformer.
*/
public class ContinueDocXMLStreamWriter implements XMLStreamWriter {
private XMLStreamWriter writer;
ContinueDocXMLStreamWriter(XMLStreamWriter writer) {
this.writer = writer;
}
@Override
public void writeStartDocument() throws XMLStreamException {
// writer.writeStartDocument();
}
@Override
public void writeStartDocument(String version)
throws XMLStreamException {
// writer.writeStartDocument(version);
}
@Override
public void writeStartDocument(String encoding, String version)
throws XMLStreamException {
// writer.writeStartDocument(encoding, version);
}
@Override
public void writeEndDocument() throws XMLStreamException {
// writer.writeEndDocument();
}
@Override
public void writeStartElement(String localName)
throws XMLStreamException {
writer.writeStartElement(localName);
}
@Override
public void writeStartElement(String namespaceURI, String localName)
throws XMLStreamException {
writer.writeStartElement(namespaceURI, localName);
}
@Override
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
writer.writeStartElement(prefix, localName, namespaceURI);
}
@Override
public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
writer.writeEmptyElement(namespaceURI, localName);
}
@Override
public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
writer.writeEmptyElement(prefix, localName, namespaceURI);
}
@Override
public void writeEmptyElement(String localName) throws XMLStreamException {
writer.writeEmptyElement(localName);
}
@Override
public void writeEndElement() throws XMLStreamException {
writer.writeEndElement();
}
@Override
public void close() throws XMLStreamException {
writer.close();
}
@Override
public void flush() throws XMLStreamException {
writer.flush();
}
@Override
public void writeAttribute(String localName, String value) throws XMLStreamException {
writer.writeAttribute(localName, value);
}
@Override
public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException {
writer.writeAttribute(prefix, namespaceURI, localName, value);
}
@Override
public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
writer.writeAttribute(namespaceURI, localName, value);
}
@Override
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
writer.writeNamespace(prefix, namespaceURI);
}
@Override
public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
writer.writeDefaultNamespace(namespaceURI);
}
@Override
public void writeComment(String data) throws XMLStreamException {
writer.writeComment(data);
}
@Override
public void writeProcessingInstruction(String target) throws XMLStreamException {
writer.writeProcessingInstruction(target);
}
@Override
public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
writer.writeProcessingInstruction(target, data);
}
@Override
public void writeCData(String data) throws XMLStreamException {
writer.writeCData(data);
}
@Override
public void writeDTD(String dtd) throws XMLStreamException {
writer.writeDTD(dtd);
}
@Override
public void writeEntityRef(String name) throws XMLStreamException {
writer.writeEntityRef(name);
}
@Override
public void writeCharacters(String text) throws XMLStreamException {
writer.writeCharacters(text);
}
@Override
public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
writer.writeCharacters(text, start, len);
}
@Override
public String getPrefix(String uri) throws XMLStreamException {
return writer.getPrefix(uri);
}
@Override
public void setPrefix(String prefix, String uri) throws XMLStreamException {
writer.setPrefix(prefix, uri);
}
@Override
public void setDefaultNamespace(String uri) throws XMLStreamException {
writer.setDefaultNamespace(uri);
}
@Override
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
writer.setNamespaceContext(context);
}
@Override
public NamespaceContext getNamespaceContext() {
return writer.getNamespaceContext();
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
return writer.getProperty(name);
}
}
这是我的测试程序:
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stax.StAXResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class NoDeclXmlFailed {
public static void main(String ... args) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
XMLOutputFactory output = XMLOutputFactory.newInstance();
File destination = File.createTempFile("example_", ".xml");
XMLStreamWriter writer = output.createXMLStreamWriter(new FileOutputStream(destination));
writer.writeStartDocument();
writer.writeStartElement("test");
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
doc.setXmlStandalone(true);
Element foo = doc.createElement("foo");
foo.setTextContent("bar");
foo.setAttribute("wibble", "wobble");
DOMSource source = new DOMSource(foo);
StAXResult stax = new StAXResult(new ContinueDocXMLStreamWriter(writer));
transformer.transform(source, stax);
foo = doc.createElement("foo");
foo.setTextContent("bar2");
foo.setAttribute("wibble", "wobble2");
source = new DOMSource(foo);
stax = new StAXResult(new ContinueDocXMLStreamWriter(writer));
transformer.transform(source, stax);
writer.writeEndDocument();
writer.flush();
writer.close();
Files.lines(destination.toPath())
.forEach(line -> System.out.println(line));
destination.delete();
}
}