为什么 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

我的错误在哪里,怎样才能得到预期的结果?

编辑:

在内部,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 的数据可用性。