package org.caudexorigo.xml; import static javax.xml.stream.XMLStreamConstants.CHARACTERS; import static javax.xml.stream.XMLStreamConstants.DTD; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; import java.io.Closeable; import java.io.InputStream; import java.util.Iterator; import java.util.NoSuchElementException; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; public class StreamingUnmarshaller<T> implements Iterable<T>, Iterator<T>, Closeable { XMLStreamReader reader; Class<T> clazz; Unmarshaller unmarshaller; public StreamingUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException { this.clazz = clazz; this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller(); this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream); /* ignore headers */ skipElements(START_DOCUMENT, DTD); /* ignore root element */ reader.nextTag(); /* if there's no tag, ignore root element's end */ skipElements(END_ELEMENT); } @Override public T next() { if (!hasNext()) throw new NoSuchElementException(); try { T value = unmarshaller.unmarshal(reader, clazz).getValue(); skipElements(CHARACTERS, END_ELEMENT); return value; } catch (XMLStreamException | JAXBException e) { throw new RuntimeException(e); } } @Override public boolean hasNext() { try { return reader.hasNext(); } catch (XMLStreamException e) { throw new RuntimeException(e); } } @Override public void close() { try { reader.close(); } catch (XMLStreamException e) { // ignore since we are closing the reader } } private void skipElements(int... elements) throws XMLStreamException { int eventType = reader.getEventType(); while (arrayContains(elements, eventType)) { eventType = reader.next(); } } private boolean arrayContains(int[] searchArray, int value) { for (int ix : searchArray) { if (ix == value) { return true; } } return false; } @Override public Iterator<T> iterator() { return this; } }