【问题标题】:Omitting XML Declaration when invoking Transformer with StAXResult使用 StAXResult 调用 Transformer 时省略 XML 声明
【发布时间】:2012-03-24 19:06:07
【问题描述】:

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

每个文件应该包含 header 节点,恰好一个 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 声明的片段)?

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

【问题讨论】:

    标签: xml-parsing stax transformer


    【解决方案1】:

    Xalan 的SAX2StAXStreamWriter 正在这样做。另一个 XSLT 实现的行为可能不同。为了解决这个问题,你可以包装你的 writer 并强制 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) {}
    };
    

    应该可以解决问题。

    【讨论】:

      【解决方案2】:

      根据@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();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2010-12-13
        • 2011-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        相关资源
        最近更新 更多