StAX lets the caller pull XML events one at a time. That limits retained tree state, but the caller still owns input size, entity policy, event count, and reader closure.
Java StAX: pull XML events under a count and entity policy
Operational contract
The factory disables DTD support and external entities, then the code counts receipt start elements. It rejects more than 47 receipts and closes the XMLStreamReader in finally. The source is a bounded byte array, so this method does not open an external stream. A provider may reject a requested property; fail that setup rather than ignoring it. Pull parsing is useful when the workflow needs only selected values, but an XML grammar and depth limit remain separate requirements. If an InputStream were supplied by a caller, closing the reader would not be a safe substitute for an explicit stream-ownership agreement.
Failure case
A depot accepts a manifest with 47 receipt elements. A 48th makes the document invalid for this intake, even if the XML is syntactically sound. The count is a business limit, not a parser feature. The implementation does not keep the document tree merely to compute that count.
Java code
import java.io.ByteArrayInputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
public class ReceiptStaxCounter {
public static int count(byte[] xml) throws Exception {
if (xml.length > 47_000) throw new IllegalArgumentException("XML exceeds cap");
XMLInputFactory factory = XMLInputFactory.newFactory();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
XMLStreamReader reader = factory.createXMLStreamReader(new ByteArrayInputStream(xml));
try {
int receipts = 0;
while (reader.hasNext()) {
if (reader.next() == XMLStreamConstants.START_ELEMENT
&& "receipt".equals(reader.getLocalName())) {
if (++receipts > 47) throw new IllegalArgumentException("Too many receipts");
}
}
return receipts;
} finally {
reader.close();
}
}
}Performance and ownership cost
The scan is O(E) over E XML events and retains O(1) application state beyond parser buffers. The 47,000-byte input cap bounds the source array, while parser internals and deeply nested input still merit separate limits.
Common Mistakes
- Do not assume streaming removes the need for input and depth caps.
- Do not ignore a provider's refusal to disable external entities.
- Do not leave the XMLStreamReader open after a failure.
Connected lessons
- Java DOM parsing: deny external XML access at the factory
- Java Stream.close: terminal traversal does not close every source
- Java bounded line reader: reject oversized records without buffering the file
- Java XPath: compile a fixed selection, not user-supplied code
- Java XML Schema validation: use a trusted schema without external fetches
- Java XML Transformer: bound serialized output and external access
- Java XML namespaces: select by URI and local name
- Java XML and archive boundaries quiz
- Advanced Java
