Чтение большого XML файла с использованием stax и dom
Мне нужно прочитать несколько больших (200Mb-500Mb) XML файлов, поэтому я хочу использовать StaX. Моя система имеет два модуля - один для чтения файла (с помощью StaX); другой модуль (модуль "синтаксический анализатор"), чтобы получить одну запись этого XML и проанализировать его с помощью DOM. Мои XML файлы не имеют определенной структуры, поэтому я не могу использовать JaxB. Как я могу передать модулю "парсер" определенную запись, которую я хочу, чтобы он разбирался? Например:
<Items>
<Item>
<name> .... </name>
<price> ... </price>
</Item>
<Item>
<name> .... </name>
<price> ... </price>
</Item>
</Items>
Я хочу использовать StaX для разбора этого файла, но каждый элемент "item" будет передан модулю "parser".
Редактировать:
После немного большего чтения - я думаю, мне нужна библиотека, которая читает XML файл с использованием потока, - но анализируйте каждую запись с помощью DOM. Что-то подобное существует?
Ответы
Ответ 1
Вы можете использовать парсер SAX (javax.xml.stream
) и преобразовать (javax.xml.transform
) каждый раздел в узел DOM (org.w3c.dom
):
import java.io.*;
import javax.xml.stream.*;
import javax.xml.transform.*;
import javax.xml.transform.stax.StAXSource;
import javax.xml.transform.dom.DOMResult;
import org.w3c.dom.*
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
xsr.nextTag(); // Advance to statements element
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
while(xsr.nextTag() == XMLStreamConstants.START_ELEMENT) {
DOMResult result = new DOMResult();
t.transform(new StAXSource(xsr), result);
Node domNode = result.getNode();
}
}
}
Также см:
Ответ 2
Ответ Блэйза Дафана не проходит в чистых java 7 и 8 из-за https://bugs.openjdk.java.net/browse/JDK-8016914
java.lang.NullPointerException
at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.setXmlVersion(CoreDocumentImpl.java:860)
at com.sun.org.apache.xalan.internal.xsltc.trax.SAX2DOM.setDocumentInfo(SAX2DOM.java:144)
Забавная вещь: если вы используете jaxb unmarshaller, вы не получите NPE:
package com.common.config;
import java.io.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.*;
import org.w3c.dom.*;
public class Demo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newInstance();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileReader("input.xml"));
// Advance to root element
xsr.nextTag(); // TODO: nextTag() can't skip DTD
xsr.next(); // Advance to first item or EOD
final JAXBContext jaxbContext = JAXBContext.newInstance();
final Unmarshaller unm = jaxbContext.createUnmarshaller();
while(true) {
// previous unmarshal() already did advance to next element or whitespace
if (xsr.getEventType() == XMLStreamReader.START_ELEMENT) {
JAXBElement<Object> jel = unm.unmarshal(xsr, Object.class);
Node domNode = (Node)jel.getValue();
System.err.println(domNode.getNodeName());
} else if (!xsr.hasNext()) {
break;
} else {
xsr.next();
}
}
}
}
Причина в том, что com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXConnector$1
не реализует Locator2
поэтому у него нет getXMLVersion()
.
Ответ 3
вы можете попробовать XMLDog из JLibs.
Он оценивает xpath в XML-документе, используя SAX (т.е. без загрузки всего xml в память). и возвращает узлы dom для узлов по мере их попадания.
таким образом, вы можете оценить xpath/Items/Item на вашем жирном XML-документе. вы будете уведомлены о том, что каждый элемент узла разбирается. вы можете обработать текущий элемент dom dom и продолжить.
Таким образом, он подходит для оценки xpaths на больших документах