Java 为什么SAXParser在抛出事件之前读了这么多?

Java 为什么SAXParser在抛出事件之前读了这么多?,java,xml,stream,sax,saxparser,Java,Xml,Stream,Sax,Saxparser,场景:我通过极慢的网络接收到一个巨大的xml文件,所以我希望尽早开始过度处理。因此,我决定使用SAXParser 我希望在标记完成后,我会得到一个事件 下面的测试说明了我的意思: @Test public void sax_parser_read_much_things_before_returning_events() throws Exception{ String xml = "<a>" + " <b>..</b>

场景:我通过极慢的网络接收到一个巨大的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);
        }
    });
}
@测试
public void sax\u解析器\u在返回\u events()引发异常之前读取\u much\u things\u{
字符串xml=“”
+ "  .."
+ "  .."
//更多。。。
+ "";
//包装器以显示所读内容
InputStream is=新的InputStream(){
InputStream is=newbytearrayinputstream(xml.getBytes());
@凌驾
public int read()引发IOException{
int val=is.read();
系统输出打印((字符)val);
返回val;
}
};
SAXParser parser=SAXParserFactory.newInstance().newSAXParser();
parser.parse(是新的DefaultHandler()){
@凌驾
public void startElement(字符串uri、字符串localName、字符串qName、属性)引发SAXException{
System.out.print(“\n引导程序开始:“+qName”);
}
@凌驾
public void endElement(字符串uri、字符串localName、字符串qName)引发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
...

在内部,SAX解析器很可能已将您的输入流包装在
缓冲读取器中
或使用某种缓冲。否则它将从输入中读取单个字节,这将真正影响性能


因此,您看到的是解析器从输入中读取一个块,然后处理该部分,发出SAX事件,等等…

似乎您对I/O的工作方式做出了错误的假设。与大多数软件一样,XML解析器将请求数据块,因为从流中请求单个字节会导致性能灾难

这并不意味着在读取尝试返回之前缓冲区必须完全填满。只是,
ByteArrayInputStream
无法模拟网络
InputStream
的行为。您可以通过覆盖
读取(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);
        }
    });
}
@测试
public void sax\u解析器\u在返回\u events()引发异常之前读取\u much\u things\u{
最后一个字符串xml=“”
+ "  .."
+ "  .."
//更多。。。
+ "";
//包装器以显示所读内容
InputStream is=新的InputStream(){
InputStream is=newbytearrayinputstream(xml.getBytes());
@凌驾
public int read()引发IOException{
int val=is.read();
系统输出打印((字符)val);
返回val;
}
@凌驾
公共整数读取(字节[]b,整数关闭,整数长度)引发IOException{
返回super.read(b,off,1);
}
};
SAXParser parser=SAXParserFactory.newInstance().newSAXParser();
parser.parse(是新的DefaultHandler()){
@凌驾
public void startElement(字符串uri、字符串localName、字符串qName、属性)引发SAXException{
System.out.print(“\n引导程序开始:“+qName”);
}
@凌驾
public void endElement(字符串uri、字符串localName、字符串qName)引发SAXException{
System.out.print(“\nHandler end:+qName”);
}
});
}
这会打印出来


处理程序启动:一个
处理程序开始:b。。
处理器端:b
处理程序开始:c。。
处理器端:c
处理程序结束:a?

显示XML解析器如何适应来自
InputStream

的数据可用性我认为您应该尝试bugger测试文件-我怀疑缓冲读取在开始处理之前有效地读取了整个文件,因为它会将文件缓冲(例如)1k块或任何东西-如果你使用一个大文件,你可能会得到更像你期望的东西。
read(byte[],int,int)
可以简化为
return super.read(b,off,1)
@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);
        }
    });
}