场景:我通过极慢的网络接收一个巨大的xml文件,所以我想尽早开始过度处理。因此,我决定使用SAXParser。
我期望在标签完成后,我将得到一个事件。
下面的测试说明了我的意思:
@Test
public void sax_parser_read_much_things_before_returning_events() throws Exception{
String xml = "<a>"
+ " <b>..</b>"
+ " <c>..</c>"
// much more ...
+ "</a>";
// wrapper to show what is read
InputStream is = new InputStream() {
InputStream is = new ByteArrayInputStream(xml.getBytes());
@Override
public int read() throws IOException {
int val = is.read();
System.out.print((char) val);
return val;
}
};
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(is, new DefaultHandler(){
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.print("nHandler start: " + qName);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.print("nHandler end: " + qName);
}
});
}
我包装了输入流,以查看读取的内容和事件发生的时间。
我所期望的是这样的:
<a> <- output from read()
Handler start: a
<b> <- output from read()
Handler start: b
</b> <- output from read()
Handler end: b
...
可悲的是,结果如下:
<a> <b>..</b> <c>..</c></a> <- output from read()
Handler start: a
Handler start: b
Handler end: b
Handler start: c
Handler end: c
Handler end: a
我的错误在哪里,我怎样才能得到预期的结果?
编辑:- 第一件事是他试图检测文档版本,这导致扫描一切。与doc版本,他打破之间(但不是我期望的)
- 他"想要"读取例如1000字节和块这么长时间是不好的,因为流可能在这个时间点上没有包含这么多。
- 我在XMLEntityManager中找到了缓冲区大小:
- public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
- public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
似乎您对I/O的工作方式做出了错误的假设。像大多数软件一样,XML解析器将以块为单位请求数据,因为从流中请求单个字节会导致性能灾难。
这并不意味着在读取尝试返回之前缓冲区必须被完全填满。只是,ByteArrayInputStream
无法模仿网络InputStream
的行为。你可以很容易地解决这个问题,通过覆盖read(byte[], int, int)
,而不是返回一个完整的缓冲区,但,例如,一个字节的每个请求:
@Test
public void sax_parser_read_much_things_before_returning_events() throws Exception{
final String xml = "<a>"
+ " <b>..</b>"
+ " <c>..</c>"
// much more ...
+ "</a>";
// wrapper to show what is read
InputStream is = new InputStream() {
InputStream is = new ByteArrayInputStream(xml.getBytes());
@Override
public int read() throws IOException {
int val = is.read();
System.out.print((char) val);
return val;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return super.read(b, off, 1);
}
};
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
parser.parse(is, new DefaultHandler(){
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
System.out.print("nHandler start: " + qName);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
System.out.print("nHandler end: " + qName);
}
});
}
打印
<a>
Handler start: a<b>
Handler start: b..</b>
Handler end: b <c>
Handler start: c..</c>
Handler end: c</a>
Handler end: a?
显示了XML解析器如何适应InputStream
中数据的可用性。
SAX解析器内部最有可能将您的InputStream包装在BufferedReader
中或使用某种缓冲。否则,它将从输入中读取单个字节,这将严重影响性能。
因此,您所看到的是解析器从输入中读取一个块,然后处理该部分,发出SAX事件,等等…