2

我想将多个 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>

每个文件都应该包含节点,恰好一个节点都包含在根节点中。

现在我的问题如下:我试图通过 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 的值为“yes”)。

所以简而言之,当 StAXResult 作为目标结果时,似乎在 Transformer 上全局设置的这些设置被忽略了。

我的问题是:有什么方法可以实现这一点,以便 Transformer 不会在每次调用 Transformer.transform() 时发出 XML 声明(即编写没有 XML 声明的片段)?

非常感谢和需要您的帮助。

4

2 回答 2

2

Xalan'sSAX2StAXStreamWriter正在这样做。另一个 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) {}
};

应该做的伎俩。

于 2012-03-30T09:38:41.970 回答
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();
    }
}
于 2021-06-22T23:40:40.190 回答