【发布时间】:2013-06-02 11:54:56
【问题描述】:
我想写一些基本上类似于Reading a big XML file using stax and dom 的代码 但使用 XMLEventReader 而不是 XMLStreamReader(在继续创建 DOM 之前,我需要能够检查某些元素的值)。
有没有人有一个最小的例子来说明这看起来如何?到目前为止,我尝试过的所有操作都会出现错误或空指针异常。
谢谢! 爱洛
【问题讨论】:
我想写一些基本上类似于Reading a big XML file using stax and dom 的代码 但使用 XMLEventReader 而不是 XMLStreamReader(在继续创建 DOM 之前,我需要能够检查某些元素的值)。
有没有人有一个最小的例子来说明这看起来如何?到目前为止,我尝试过的所有操作都会出现错误或空指针异常。
谢谢! 爱洛
【问题讨论】:
看看:http://www.vogella.com/articles/JavaXML/article.html#javastax_read
它提供了一个很好的小例子,如何使用 xml-streaming 和 XMLEventReader
【讨论】:
我遇到了同样的问题,据我调试,一切都表明 JDK 中存在错误(至少在构建 1.8.0_162-b12 上),更具体地说是在 com.sun.org.apache.xalan.internal.xsltc.trax.StAXEvent2SAX 类中。
NPE实际上只是另一个bug的结果,这与该类的bridge()方法中如何处理阅读器有关。如果读者不是处于START_DOCUMENT 状态,那么下一个事件只会在第一次使用nextEvent() 时被偷看但不会被推进。这导致第一个START_ELEMENT 事件被处理两次。如果您使用StreamResult 而不是DOMResult,则可以很好地观察到这一点。那里不会发生 NPE,但结果流中生成的 XML 将包含两次第一个元素的标记的开头。
我现在正在尝试使用接收DOMResult 的XmlEventWriter 来解决此问题。因此,基本上模拟Transformer 会做什么,将每个读取事件直接推送给该作者。如果我成功了,我也会在这里发布我的解决方案。
PS:我想在 JDK 上报告这个问题,或者最终推一个潜在的解决方案。如果有人能告诉我这应该怎么做,我将不胜感激。
所以,我设法通过上述方法解决了这个问题。根据Reading a big XML file using stax and dom中建议的代码,您可以使用以下方法,而不是使用Transformer:
private Node readToNode(final XMLEventReader reader) throws XMLStreamException, ParserConfigurationException {
XMLEvent event = reader.peek();
if (!event.isStartElement()) {
throw new IllegalArgumentException("reader must be on START_ELEMENT event");
}
final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
final XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(new DOMResult(document));
int depth = 0;
do {
event = reader.nextEvent();
writer.add(event);
if (event.isStartElement()) {
depth++;
} else if (event.isEndElement()) {
depth--;
}
} while (reader.hasNext() && !(event.isEndElement() && depth <= 0));
return document.getDocumentElement();
}
但是,这种方法有一些限制!从代码中可以看出,我们需要创建一个包装节点的Document 对象,否则 XML 编写器会遇到问题。如果您打算再次使用Transformer 操作此DOM 并随后将其发送到另一个活动的XMLEventWriter(正如我试图做的那样),它将失败。这是因为Transformer 将向已经启动的编写器发送一个 START_DOCUMENT 事件。我反过来尝试了同样的方法,我。 e.将节点包装成DOMSource,将其发送到另一个XmlEventReader 并将事件通过管道传输到我现有的XmlEventWriter,但这也不起作用,因为XmlEventReader 显然只支持StreamSources(参见here )。
总而言之,如果您只需要 DOM 对象,这可能会很好,但如果您尝试将 XML 片段转换为将事件管道传输到写入器(就像我一样),您可能会遇到问题。
【讨论】: