为什么SAXParser在抛出事件之前要读取这么多内容?



场景:我通过极慢的网络接收一个巨大的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_BUFFER_SIZE = 8192;
    • 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事件,等等…

    相关内容

    • 没有找到相关文章

    最新更新