如何通过多个处理程序处理 XML 事件的 InputStream 并使用 StAX 处理 Java 中的另一个输入流?

How can I process InputStream of XML events through several handlers and to another input stream in Java using StAX?

我需要使用 StAX 解析 XML 文件(因为文件太大,无法将它们保存在内存中)并在此过程中转换某些事件/元素。我手头有一个与 XML 文件关联的 InputStream。我的想法是设计几个处理程序,通过所有处理程序将输入流提供给另一个输入流(因为转换的客户端需要一个输入流)。到目前为止,我已经想出了以下代码:

所有处理程序实现的接口:

public interface EventHandler {

  XMLEvent process(XMLEvent event);
}

像下面这样的几个处理程序。例如,一个这样的处理程序可以向元素添加属性,另一个处理程序可以以某种方式转换元素的文本等。

public class Handler1 implements EventHandler {

  @Override
  public XMLEvent process(XMLEvent event) {
    if (supports(event)) {
      // processing logic omitted
    }
  }

  private boolean supports(XMLEvent event) {
    // condition to process an event omitted
  }
}

获取原始输入流和returns处理后的输入流的处理class:

public class XmlProcessor {
  
  @Autowired
  private Set<EventHandler> eventHandlers;

  public InputStream process(InputStream inputStream) {
    InputStream result;
    XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
    XMLStreamReader xmlReader = xmlInputFactory.createXMLStreamReader(inputStream);
    XMLEvent event = null;
    while (xmlReader.hasNext()) {
      event = xmlReader.next();
      for (EventHandler eh : eventHandlers) {
        event = eh.process(event);
      }
      // here I need to somehow add the processed event to the resulting input stream
      // that I will return from this method.
    }
    return result;
  }
}

我一直在努力寻找一种方法将已处理的事件提供给结果 InputStream。我该怎么做呢?我是否需要在另一个线程中 运行 循环 while 并为此过程使用 PipedInputStreamPipedOutputStream,或者我可以在单个线程中实现吗?

在我看来,以推模式(数据提供者向接收者发出“发送”调用)而不是拉模式(接收者向接收者发出“readNext”调用)实现管道通常更简单供应商)。当没有一对一的事件对应时,这通常会使事情变得更简单,例如,当第一个过滤步骤中的一个事件在下一个过滤步骤中变成多个事件时。

但是,无论您使用的是拉模式还是推模式,管道中各阶段之间传递的对象应该是 XML 事件流,而不是字节流。如果你传递一个字节流,那么管道中的每个阶段都将不得不进行 XML 解析和序列化,这使得它非常低效。使用字节流的唯一好处是,如果管道的各个阶段 运行 在不同的进程中可能在不同的机器上,因此它们不共享内存。

你观察得对,如果流水线中的一个阶段想从上一个阶段拉出并推到下一个阶段(也就是说,如果它想拥有控制循环),那么每个阶段都在进行必须在单独的线程中 运行——除非您使用的是支持协程的编程语言,例如 C# 或带有“yield”结构的 Javascript。

另一种选择是将转换步骤编写为 XSLT 3.0 流式转换。已经产生了 XSLT 3.0 流的两个实现:Saxon 在内部以推模式工作,Exselt(当前不可用)在内部以拉模式工作,但是如果您的代码在 XSLT 中,那么您将不受此影响,因为您自己的代码完全是声明性的.

另见我的论文你拉,我推:关于管道的极性 发表于 Balisage 2009 (http://www.balisage.net/Proceedings/vol3/html/Kay01/BalisageVol3-Kay01.html)