为啥 SAXParser 在引发事件之前会读取这么多内容?
Posted
技术标签:
【中文标题】为啥 SAXParser 在引发事件之前会读取这么多内容?【英文标题】:Why does SAXParser read so much before throwing events?为什么 SAXParser 在引发事件之前会读取这么多内容? 【发布时间】:2016-01-18 23:22:08 【问题描述】:场景:我正在通过极慢的网络接收一个巨大的 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
我的错误在哪里,我怎样才能得到预期的结果?
编辑:
第一件事是他试图检测文档版本,这会导致扫描所有内容。使用文档版本,他介于两者之间(但不是我期望的) 他“想要”读取例如 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;【问题讨论】:
我认为您应该尝试一个错误的测试文件-我怀疑缓冲读取在开始处理之前有效地读取了您的整个文件,因为它会以(例如)1k 块或其他方式缓冲文件-如果你使用一个大文件,你可能会得到更像你期望的东西。 【参考方案1】:您似乎对 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
的数据可用性。
【讨论】:
read(byte[], int, int)
可以简化为return super.read(b, off, 1);
。【参考方案2】:
在内部,SAX 解析器很可能已将您的 InputStream 包装在 BufferedReader
中或使用某种缓冲。否则它会从输入中读取单个字节,这会真正影响性能。
所以您看到的是解析器从输入中读取一个块,然后处理该部分,发出 SAX 事件,等等......
【讨论】:
以上是关于为啥 SAXParser 在引发事件之前会读取这么多内容?的主要内容,如果未能解决你的问题,请参考以下文章
TreeViewItem 中未引发“MouseLeftButtonDown”事件,为啥?
为啥 GridView 中的 LinkButton 不会引发其 OnClick 事件?