有没有适用于Java的基于推送/非阻塞的XML解析器?

17

我正在寻找一个XML解析器,它可以接收文本块而不是从InputStream或InputSource中解析。 例如,我想要类似以下的东西:

public class DataReceiver {
    private SAXParser parser = //...
    private DefaultHandler handler = //...

    /**
     * Called each time some data is received.
     */
    public void onDataReceived(byte[] data) {
        parser.push(data, handler);
    }
}

我之所以想要这样做,是因为我希望使用NIO网络库而不必回到每个连接模型所需的阻塞InputStream。


了解你的 XML 文档有多长是很有趣的。 - bill
我没有任何XML文档,我正在考虑实现一个XMPP服务器,因此我正在寻找与NIO网络库良好配合的东西。 - Michael Barker
看看用Java编写的开源XMPP服务器是如何解决这个问题的可能是一个好主意。Tigase和OpenFire是我首先想到的候选人。 - Malax
1
叹息,我有同样的问题。这就是为什么所有运行时都应该有call-cc。然后我们就可以通过实现InputStream来获得这个。 - Daniel Earwicker
7个回答

7
令人惊讶的是,没有人提到一种实现非阻塞(“异步”)解析的Java XML解析器:Aalto。部分原因可能是缺乏文档(以及其低活动水平)。Aalto实现了基本的Stax API,但也有一些小的扩展,允许推入输入(这部分尚未最终确定;功能存在,但API尚未最终确定)。 要获取更多信息,您可以查看相关的讨论组

1
Aalto 最近也被重新授予 Apache 许可证:http://github.com/FasterXML/aalto-xml - Alex Cruise
附带一篇博客文章,解释如何使用非阻塞API扩展:http://www.cowtowncoder.com/blog/archives/2011/03/entry_451.html - StaxMan
1
这应该是被选中的答案。我们正在生产中使用它,并且非常满意。 - Wilfred Springer
很好知道——我没有得到太多有关使用的反馈,所以很高兴听到 Aalto 被使用了(在 http://tech.groups.yahoo.com/group/aalto-xml-interest/ 有一个 Yahoo 讨论组)。 - StaxMan
1
Aalto非常好用,我已经发布了一些辅助类:https://github.com/skjolber/async-stax-utils - ThomasRS
显示剩余2条评论

4

编辑:现在我明白了。您会分块接收XML并希望将其提供给适当的XML解析器。因此,您需要一个对象,在一端是队列,在另一端是InputStream?

您可以将接收到的字节数组聚合到ByteArrayOutputStream中,将其转换为ByteArrayInputStream并将其提供给SAXParser。

或者,您可以查看PipedInputStream/PipedOutputStream对。在这种情况下,您需要在另一个线程中执行解析,因为SAX解析器使用当前线程来发出事件,从而阻塞了您的receive()。

编辑:根据评论,我建议采用聚合路线。您将块收集到ByteArrayOutputStream中。要知道是否接收了XML的所有块,请检查当前块或ByteArrayOutputStream的内容是否包含XML根节点的结束标记。然后,您只需将数据传递给SAXParser,它现在可以在当前线程中运行而没有问题。为避免不必要的数组重新创建,您可以实现自己的非同步简单字节数组包装器或寻找这样的实现。


PipedInputStream/PipedOutputStream 看起来是提供传统解析器使用 InputStream 并推入数据块的好方法。 - Robin
这种方法仍需要为每个客户端连接使用一个线程,只是将问题转移到代码的不同区域。如果我选择每个连接一个线程模型,那么我可能会只使用传统的Socket和InputStream,因为解决方案会更简单。 - Michael Barker
我不知道。我从未听说过Java解析器有你要找的这种功能。我猜想,如果没有类似于C# yield return结构的等价物,实现这样一个解析器将非常困难——例如停止解析并返回给调用者,然后稍后继续解析是一个复杂的状态机问题。 - akarnokd

3

这是来自Xerces J-Users邮件列表的一篇(2009年4月)帖子,原帖作者遇到了完全相同的问题。 "Jeff"提供了一个可能非常好的回应,但没有对原帖作者的回应进行跟进:

http://www.nabble.com/parsing-an-xml-document-chunk-by-chunk-td22945319.html

这可能是新的足以在列表中引起注意,或至少对搜索有所帮助。

编辑

发现另一个有用的链接,提到了一个名为Woodstox的库,描述了基于流和NIO的解析器的状态,以及一些模拟流的可能方法:

http://markmail.org/message/ogqqcj7dt3lwkbov


好的发现。给了我一个想法。由于Xerces是开源的,获取它(或实际上任何足够小的开源XML解析器)并攻击从输入流读取字节的位置,并创建一个聪明的方式来状态保存/恢复以允许早期返回/继续。但我不知道如何实现它。 - akarnokd
可能对那次黑客攻击的判断是正确的。我也给原始帖子的发布者发了邮件并把这个主题帖发送给他。我希望他已经想出解决方案并愿意分享。祈祷 - Harlan Iverson

1

由于这个问题在相关的谷歌搜索中仍然很高,因此添加另一个答案 - aalto-xml 0.9.7(2011年3月)具有异步XML解析。这允许您传递文档的任意大小块以继续解析,并引入了一个新的StaX事件类型EVENT_INCOMPLETE,以指示输入缓冲区已耗尽且文档仍不完整。

这是Tatu Salorant(作者)的示例:

     byte[] msg = "<html>Very <b>simple</b> input document!</html>".getBytes();
      AsyncXMLStreamReader asyncReader = new InputFactoryImpl().createAsyncXMLStreamReader();
      final AsyncInputFeeder feeder = asyncReader.getInputFeeder();
      int inputPtr = 0; // as we feed byte at a time
      int type = 0;

      do {
        // May need to feed multiple "segments"
        while ((type = asyncReader.next()) == AsyncXMLStreamReader.EVENT_INCOMPLETE) {
          feeder.feedInput(msg, inputPtr++, 1);
          if (inputPtr >= msg.length) { // to indicate end-of-content (important for error handling)
            feeder.endOfInput();
          }
        }
        // and once we have full event, we just dump out event type (for now)
        System.out.println("Got event of type: "+type);
        // could also just copy event as is, using Stax, or do any other normal non-blocking handling:
        // xmlStreamWriter.copyEventFromReader(asyncReader, false);
      } while (type != AsyncXMLStreamReader.END_DOCUMENT);

1

1

查看openfire的XMLLeightweightParser以及它如何通过NIO从单个块生成XML消息。整个项目是关于NIO和XMPP问题答案的绝佳资源。


这是我所见过的最接近的了,不幸的是它们并没有将其作为单独的库提供,也不会将其转换为 XML 对象(元素、属性等)。 - Michael Barker
XMLLeightweightParser可以轻松地独立使用。它只是确保您拥有完整的xml标签。然后,Openfire将这些块馈送到XPP3中以解析完整的Element对象。我在受XMPP启发的服务器中也使用了同样的东西,它运行得非常好。 - daxel
这仍然需要读取比严格必要的更长的块,因为xpp3使用阻塞io(像大多数其他拉解析器一样)。如果您明确地框架了片段,则可能比完全阻止更好,但并不完全理想。 - StaxMan

0
对不起,我没能解决这个问题。 我找不到一个像我需要的解析器。 但我正在考虑自己编写一个。 一个非常简单的:只是像可行性研究一样,但足以解决我的问题和希望你的问题。 不幸的是,我一直很忙,接下来的两周我会离开, 但也许在七月份我会开始着手解决。 我一有结果就会尽快告诉你。

如果你这样做了,请告诉我。如果你能够将它开源,我希望能够做出贡献。 - Michael Barker
我不确定,但也许 Yielder 框架能够帮助解决这个问题?请参考 http://chaoticjava.com/posts/category/code/java/frameworks/yielder/ 了解详情。然而,它的内部工作方式相当丑陋,因为它使用了字节码重构来支持早期返回、暂停执行、稍后继续的格式。 - akarnokd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接