是否可以创建 XSL 转换器输出流?

Is it possible to create an XSL Transformer Output Stream?

是否可以创建 "TransformerOutputStream",扩展标准 java.io.OutputStream,包装提供的输出流并应用 XSL 转换?我找不到任何允许我执行此操作的 API 组合。

关键是,一旦创建,TransformerOutputStream 就可以传递给接受标准 java.io.OutputStream 的其他 API。

最小的用法是这样的:

java.io.InputStream in = getXmlInput();
java.io.OutputStream out = getTargetOutput();

javax.xml.transform.Templates templates = createReusableTemplates();        // could also use S9API
TransformerOutputStream tos = new TransformerOutputStream(out, templates);  // extends OutputStream

com.google.common.io.ByteStreams.copy(in, tos);

// possibly flush/close tos if required by implementation

这是一个 JAXP 示例,但由于我目前正在使用 Saxon,因此 S9API 解决方案也可以。

我遵循的主要途径是:

但我找不到其中任何一个的实现,这似乎表明要么没有其他人尝试过这样做,要么存在一些使其不切实际的问题,要么我的搜索技巧不是那样不错。

我可以理解,对于某些模板,XML 转换器可能需要访问整个文档,因此 SAX 内容处理程序可能没有任何优势,但还必须有可以应用于流过时流?这种接口会将决定留给转换器实现。

我写了一个,目前正在使用提供此接口的 class,但它只是在内部缓冲区中收集输出数据,然后使用标准 JAXP StreamSource 在刷新时读取它或关闭,因此最终会缓冲整个文档。

您可以让您的 TransformerOutputStream 扩展 ByteArrayOutputStream,它的 close() 方法可以获取底层 byte[] 数组,将其包装在 ByteArrayInputStream 中,并使用从该 InputStream 获取的输入调用转换。

但您似乎还想避免将流的全部内容放入内存中。因此,让我们假设您要应用的转换是 XSLT 3.0 流式转换。不幸的是,尽管作为流式 XSLT 转换器的 Saxon 主要以推送模式运行("push" 我的意思是数据提供者调用数据消费者,而 "pull" 意味着数据消费者调用数据提供者),第一阶段,即读取和解析输入,始终处于拉取模式——我不知道可以将词法 XML 输入推送到的 XML 解析器。

这意味着这里存在 push-pull 冲突。 push-pull 冲突有两种解决方案。一种是将数据缓存在内存中(也就是前面提到的ByteArrayOutputStream的做法)。另一种是使用两个线程,一个写入共享缓冲区,另一个从中读取。这可以在写入线程中使用 PipedOutputStream (https://docs.oracle.com/javase/8/docs/api/index.html?java/io/PipedOutputStream.html) 并在读取线程中使用 PipedInputStream 来实现。

警告:我还没有真正尝试过这个,但我看不出它为什么不起作用。

请注意,XSLT 3.0 中的流式处理主题相当复杂;您需要先了解它,然后才能在这里取得很大进展。我将从 XML 伦敦 2014 年 Abel Braaksma 的演讲开始:https://xmllondon.com/2014/presentations/braaksma