是否可以创建XSL变压器输出流?

时间:2019-06-14 01:09:18

标签: java xslt xslt-2.0 saxon

是否可以创建“ 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解决方案也可以。

我说服的主要途径是:

  • 扩展java.io.OutputStream并实现org.xml.sax.ContentHandler的类
  • 基于org.xml.sax.ContentHandler的XSL转换器

但是我找不到这两种方法的实现,这似乎表明没有其他人尝试过这样做,有一些问题使它不切实际,或者我的搜索技能并不那么好。

我可以理解,使用某些模板,XML转换器可能需要访问整个文档,因此SAX内容处理程序可能没有优势,但是还必须进行简单的转换,以便在流通过时将其应用于流?这种接口会将决定权留给变压器实现。

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

1 个答案:

答案 0 :(得分:2)

您可以使TransformerOutputStream扩展ByteArrayOutputStream,并且其close()方法可以获取基础的byte []数组,将其包装在ByteArrayInputStream中,并使用从此InputStream提取的输入来调用转换。

但是似乎您也想避免将流的全部内容都放在内存中。因此,假设您要应用的转换是XSLT 3.0可流式转换。不幸的是,尽管Saxon作为流XSLT转换器主要在推模式下运行(通过“推”,我是指数据提供者调用数据使用者,而“拉”是指数据使用者调用数据提供者),但第一阶段读取和解析输入始终处于拉模式-我不知道可以将词法XML输入推送到的XML解析器。

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

注意:实际上,我还没有尝试过,但是我看不出为什么它不起作用。

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