为什么 SAXParser 在抛出事件之前读取这么多?
Why does SAXParser read so much before throwing events?
场景: 我通过极慢的网络接收到一个巨大的 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 版本,他介于两者之间(但不是我期望的)
- 他 "wants to" 读取 1000 个字节和块这么长时间是不好的,因为此时流可能不包含那么多内容。
- 我在 XMLEntityManager 中找到了缓冲区大小:
- public static final int DEFAULT_BUFFER_SIZE = 8192;
- public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
- public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
在内部,SAX 解析器很可能已将您的 InputStream 包装在 BufferedReader
中或使用某种缓冲。否则它会从输入中读取单个字节,这确实会影响性能。
所以您看到的是解析器从输入中读取一个块,然后处理该部分,发出 SAX 事件,等等...
您似乎对 I/O 的工作原理做出了错误的假设。与大多数软件一样,XML 解析器将以块的形式请求数据,因为从流中请求单个字节会导致性能灾难。
这并不意味着缓冲区必须在读取尝试之前完全填满 returns。只是,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
的数据可用性。
场景: 我通过极慢的网络接收到一个巨大的 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 版本,他介于两者之间(但不是我期望的)
- 他 "wants to" 读取 1000 个字节和块这么长时间是不好的,因为此时流可能不包含那么多内容。
- 我在 XMLEntityManager 中找到了缓冲区大小:
- public static final int DEFAULT_BUFFER_SIZE = 8192;
- public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
- public static final int DEFAULT_INTERNAL_BUFFER_SIZE = 1024;
在内部,SAX 解析器很可能已将您的 InputStream 包装在 BufferedReader
中或使用某种缓冲。否则它会从输入中读取单个字节,这确实会影响性能。
所以您看到的是解析器从输入中读取一个块,然后处理该部分,发出 SAX 事件,等等...
您似乎对 I/O 的工作原理做出了错误的假设。与大多数软件一样,XML 解析器将以块的形式请求数据,因为从流中请求单个字节会导致性能灾难。
这并不意味着缓冲区必须在读取尝试之前完全填满 returns。只是,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
的数据可用性。